#ifndef _NXD_LIBGOLANG_H #define _NXD_LIBGOLANG_H // Copyright (C) 2018-2019 Nexedi SA and Contributors. // Kirill Smelkov <kirr@nexedi.com> // // This program is free software: you can Use, Study, Modify and Redistribute // it under the terms of the GNU General Public License version 3, or (at your // option) any later version, as published by the Free Software Foundation. // // You can also Link and Combine this program with other software covered by // the terms of any of the Free Software licenses or any of the Open Source // Initiative approved licenses and Convey the resulting work. Corresponding // source of such a combination shall include the source code for all other // software used. // // This program is distributed WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // // See COPYING file for full licensing terms. // See https://www.nexedi.com/licensing for rationale and options. // Library Libgolang provides Go-like features for C and C++. // // Library Libgolang provides goroutines, channels with Go semantic and other // accompanying features. The library consists of high-level type-safe C++ API, // and low-level unsafe C API. The low-level C API was inspired by Libtask[1] // and Plan9/Libthread[2]. // // The primary motivation for Libgolang is to serve as runtime for golang.pyx - // - Cython part of Pygolang project. However Libgolang is independent of // Python and should be possible to use in standalone C/C++ projects. // // Brief description of Libgolang API follows: // // C++-level API // // - `go` spawns new task. // - `chan<T>`, and `select` provide channels with Go semantic and automatic // lifetime management. // - `panic` throws exception that represent C-level panic. // - `time::sleep` pauses current task. // - `sync::Sema` and `sync::Mutex` provide low-level synchronization. // // For example: // // chan<int> ch = makechan<int>(); // create new channel // go(worker, ch, 1); // spawn worker(chan<int>, int) // ch.send(1) // j = ch.recv() // // _ = select({ // _default, // 0 // ch.sends(&i), // 1 // ch.recvs(&j), // 2 // }); // if (_ == 0) // // default case selected // if (_ == 1) // // case 1 selected: i sent to ch // if (_ == 2) // // case 2 selected: j received from ch // // if (<bug condition>) // panic("bug"); // // // C-level API // // - `_taskgo` spawns new task. // - `_makechan` creates raw channel with Go semantic. // - `_chanxincref` and `_chanxdecref` manage channel lifetime. // - `_chansend` and `_chanrecv` send/receive over raw channel. // - `_chanselect`, `_selsend`, `_selrecv`, ... provide raw select functionality. // - `_tasknanosleep` pauses current task. // - `_makesema` and `_sema*` provide semaphore functionality. // // // Runtimes // // Libgolang, before being used, must be initialized with particular runtime // plugin, which tailors Libgolang to particular execution environment. See // `_libgolang_init` and `_libgolang_runtime_ops` for description of a runtime. // // Pygolang - the parent project of Libgolang - comes with two Libgolang runtimes: // // - "thread" - a runtime that is based on OS threads, and // - "gevent" - a runtime that is based on greenlet and gevent. // // Once again, Libgolang itself is independent from Python, and other runtimes // are possible. // // // [1] Libtask: a Coroutine Library for C and Unix. https://swtch.com/libtask. // [2] http://9p.io/magic/man2html/2/thread. #include <stdbool.h> #include <stddef.h> #include <stdint.h> // DSO symbols visibility (based on https://gcc.gnu.org/wiki/Visibility) #if defined _WIN32 || defined __CYGWIN__ #ifdef BUILDING_LIBGOLANG #define LIBGOLANG_API __declspec(dllexport) #else #define LIBGOLANG_API __declspec(dllimport) #endif #elif __GNUC__ >= 4 #define LIBGOLANG_API __attribute__ ((visibility ("default"))) #else #define LIBGOLANG_API #endif // ---- C-level API that is always available ---- // (most of the functions are documented in libgolang.cpp) #ifdef __cplusplus namespace golang { extern "C" { #endif #ifdef __cplusplus [[noreturn]] #else _Noreturn #endif LIBGOLANG_API void panic(const char *arg); LIBGOLANG_API const char *recover(void); LIBGOLANG_API void _taskgo(void (*f)(void *arg), void *arg); LIBGOLANG_API void _tasknanosleep(uint64_t dt); LIBGOLANG_API uint64_t _nanotime(void); typedef struct _chan _chan; LIBGOLANG_API _chan *_makechan(unsigned elemsize, unsigned size); LIBGOLANG_API void _chanxincref(_chan *ch); LIBGOLANG_API void _chanxdecref(_chan *ch); LIBGOLANG_API int _chanrefcnt(_chan *ch); LIBGOLANG_API void _chansend(_chan *ch, const void *ptx); LIBGOLANG_API void _chanrecv(_chan *ch, void *prx); LIBGOLANG_API bool _chanrecv_(_chan *ch, void *prx); LIBGOLANG_API void _chanclose(_chan *ch); LIBGOLANG_API unsigned _chanlen(_chan *ch); LIBGOLANG_API unsigned _chancap(_chan *ch); enum _chanop { _CHANSEND = 0, _CHANRECV = 1, _DEFAULT = 2, }; enum _selflags { // _INPLACE_DATA indicates that select case data is stored in // _selcase.itxrx instead of in *_selcase.ptxrx . // XXX can be used only for send for now. In the future, for symmetry, we // might want to allow _INPLACE_DATA for recv too. _INPLACE_DATA = 1, }; // _selcase represents one select case. typedef struct _selcase { _chan *ch; // channel enum _chanop op : 8; // chansend/chanrecv/default enum _selflags flags : 8; // e.g. _INPLACE_DATA unsigned user : 8; // arbitrary value that can be set by user // (e.g. pyselect stores channel type here) unsigned : 8; union { void *ptxrx; // chansend: ptx; chanrecv: prx uint64_t itxrx; // used instead of .ptxrx if .flags&_INPLACE_DATA != 0 }; bool *rxok; // chanrecv: where to save ok if !NULL; otherwise not used #ifdef __cplusplus // ptx returns pointer to data to send for this case. // .op must be _CHANSEND. LIBGOLANG_API const void *ptx() const; // prx returns pointer for this case to receive data into. // .op must be _CHANRECV. LIBGOLANG_API void *prx() const; #endif } _selcase; LIBGOLANG_API int _chanselect(const _selcase *casev, int casec); // _selsend creates `_chansend(ch, ptx)` case for _chanselect. static inline _selcase _selsend(_chan *ch, const void *ptx) { _selcase _ = { .ch = ch, .op = _CHANSEND, .flags = (enum _selflags)0, .user = 0xff, .ptxrx = (void *)ptx, .rxok = NULL, }; return _; } // _selrecv creates `_chanrecv(ch, prx)` case for _chanselect. static inline _selcase _selrecv(_chan *ch, void *prx) { _selcase _ = { .ch = ch, .op = _CHANRECV, .flags = (enum _selflags)0, .user = 0xff, .ptxrx = prx, .rxok = NULL, }; return _; } // _selrecv_ creates `*pok = _chanrecv_(ch, prx)` case for _chanselect. static inline _selcase _selrecv_(_chan *ch, void *prx, bool *pok) { _selcase _ = { .ch = ch, .op = _CHANRECV, .flags = (enum _selflags)0, .user = 0xff, .ptxrx = prx, .rxok = pok, }; return _; } // _default represents default case for _chanselect. extern LIBGOLANG_API const _selcase _default; // _sema corresponds to sync.Sema // no C-level analog is provided for sync.Mutex typedef struct _sema _sema; LIBGOLANG_API _sema *_makesema(void); LIBGOLANG_API void _semafree(_sema *sema); LIBGOLANG_API void _semaacquire(_sema *sema); LIBGOLANG_API void _semarelease(_sema *sema); // libgolang runtime - the runtime must be initialized before any other libgolang use. typedef struct _libgolang_sema _libgolang_sema; typedef enum _libgolang_runtime_flags { // STACK_DEAD_WHILE_PARKED indicates that it is not safe to access // goroutine's stack memory while the goroutine is parked. // // for example gevent/greenlet/stackless use it because they copy g's stack // to heap on park and back on unpark. This way if objects on g's stack // were accessed while g was parked it would be memory of another g's stack. STACK_DEAD_WHILE_PARKED = 1, } _libgolang_runtime_flags; typedef struct _libgolang_runtime_ops { _libgolang_runtime_flags flags; // go should spawn a task (coroutine/thread/...). void (*go)(void (*f)(void *), void *arg); // sema_alloc should allocate a semaphore. // if allocation fails it must return NULL. _libgolang_sema* (*sema_alloc)(void); // sema_free should release previously allocated semaphore. // libgolang guarantees to call it only once and only for a semaphore // previously successfully allocated via sema_alloc. void (*sema_free) (_libgolang_sema*); // sema_acquire/sema_release should acquire/release live semaphore allocated via sema_alloc. void (*sema_acquire)(_libgolang_sema*); void (*sema_release)(_libgolang_sema*); // nanosleep should pause current goroutine for at least dt nanoseconds. // nanosleep(0) is not noop - such call must be at least yielding to other goroutines. void (*nanosleep)(uint64_t dt); // nanotime should return current time since EPOCH in nanoseconds. uint64_t (*nanotime)(void); } _libgolang_runtime_ops; LIBGOLANG_API void _libgolang_init(const _libgolang_runtime_ops *runtime_ops); // for testing LIBGOLANG_API int _tchanrecvqlen(_chan *ch); LIBGOLANG_API int _tchansendqlen(_chan *ch); LIBGOLANG_API extern void (*_tblockforever)(void); #ifdef __cplusplus }} #endif // ---- C++-level API that is available when compiling with C++ ---- #ifdef __cplusplus #include <exception> #include <functional> #include <initializer_list> #include <memory> #include <type_traits> #include <utility> namespace golang { // go provides type-safe wrapper over _taskgo. template<typename F, typename... Argv> // F = std::function<void(Argv...)> static inline void go(F /*std::function<void(Argv...)>*/ f, Argv... argv) { typedef std::function<void(void)> Frun; Frun *frun = new Frun (std::bind(f, argv...)); _taskgo([](void *_frun) { std::unique_ptr<Frun> frun (reinterpret_cast<Frun*>(_frun)); (*frun)(); // frun deleted here on normal exit or panic. }, frun); } template<typename T> class chan; template<typename T> static chan<T> makechan(unsigned size=0); template<typename T> static chan<T> _wrapchan(_chan *_ch); // chan<T> provides type-safe wrapper over _chan. // // chan<T> is automatically reference-counted and is safe to use from multiple // goroutines simultaneously. template<typename T> class chan { _chan *_ch; public: inline chan() { _ch = NULL; } // nil channel if not explicitly initialized friend chan<T> makechan<T>(unsigned size); friend chan<T> _wrapchan<T>(_chan *_ch); inline ~chan() { _chanxdecref(_ch); _ch = NULL; } // = nil inline chan(nullptr_t) { _ch = NULL; } inline chan& operator=(nullptr_t) { _chanxdecref(_ch); _ch = NULL; return *this; } // copy inline chan(const chan& from) { _ch = from._ch; _chanxincref(_ch); } inline chan& operator=(const chan& from) { if (this != &from) { _chanxdecref(_ch); _ch = from._ch; _chanxincref(_ch); } return *this; } // move inline chan(chan&& from) { _ch = from._ch; from._ch = NULL; } inline chan& operator=(chan&& from) { if (this != &from) { _chanxdecref(_ch); _ch = from._ch; from._ch = NULL; } return *this; } // _chan does plain memcpy to copy elements. // TODO allow all types (e.g. element=chan ) static_assert(std::is_trivially_copyable<T>::value, "TODO chan<T>: T copy is not trivial"); // send/recv/close inline void send(const T &ptx) const { _chansend(_ch, &ptx); } inline T recv() const { T rx; _chanrecv(_ch, &rx); return rx; } inline std::pair<T,bool> recv_() const { T rx; bool ok = _chanrecv_(_ch, &rx); return std::make_pair(rx, ok); } inline void close() const { _chanclose(_ch); } // send/recv in select // ch.sends creates `ch.send(*ptx)` case for select. [[nodiscard]] inline _selcase sends(const T *ptx) const { return _selsend(_ch, ptx); } // ch.recvs creates `*prx = ch.recv()` case for select. // // if pok is provided the case is extended to `[*prx, *pok] = ch.recv_()` // if both prx and pok are omitted the case is reduced to `ch.recv()`. [[nodiscard]] inline _selcase recvs(T *prx=NULL, bool *pok=NULL) const { return _selrecv_(_ch, prx, pok); } // length/capacity inline unsigned len() const { return _chanlen(_ch); } inline unsigned cap() const { return _chancap(_ch); } // compare wrt nil inline bool operator==(nullptr_t) const { return (_ch == NULL); } inline bool operator!=(nullptr_t) const { return (_ch != NULL); } // compare wrt chan inline bool operator==(const chan<T>& ch2) const { return (_ch == ch2._ch); } inline bool operator!=(const chan<T>& ch2) const { return (_ch != ch2._ch); } // for testing inline _chan *_rawchan() const { return _ch; } }; // _elemsize<T> returns element size for chan<T>. template<typename T> static inline unsigned _elemsize() { return std::is_empty<T>::value ? 0 // eg struct{} for which sizeof() gives 1 - *not* 0 : sizeof(T); } // makechan<T> makes new chan<T> with capacity=size. template<typename T> static inline chan<T> makechan(unsigned size) { chan<T> ch; ch._ch = _makechan(_elemsize<T>(), size); return ch; } // _wrapchan<T> wraps raw channel with chan<T>. // raw channel must be either NULL or its element size must correspond to T. LIBGOLANG_API void __wrapchan(_chan *_ch, unsigned elemsize); template<typename T> static inline chan<T> _wrapchan(_chan *_ch) { chan<T> ch; __wrapchan(_ch, _elemsize<T>()); ch._ch = _ch; return ch; } // structZ is struct{}. // // it's a workaround for e.g. makechan<struct{}> giving // "error: types may not be defined in template arguments". struct structZ{}; // select, together with chan<T>.sends and chan<T>.recvs, provide type-safe // wrappers over _chanselect and _selsend/_selrecv/_selrecv_. // // Usage example: // // _ = select({ // ch1.recvs(&v), // 0 // ch2.recvs(&v, &ok), // 1 // ch2.sends(&v), // 2 // _default, // 3 // }) static inline // select({case1, case2, case3}) int select(const std::initializer_list<const _selcase> &casev) { return _chanselect(casev.begin(), casev.size()); } template<size_t N> static inline // select(casev_array) int select(const _selcase (&casev)[N]) { return _chanselect(&casev[0], N); } // defer(f) mimics `defer f()` from golang. // NOTE contrary to Go f is called at end of current scope, not function. #define defer(f) golang::_deferred _defer_ ## __COUNTER__ (f) struct _deferred { typedef std::function<void(void)> F; F f; _deferred(F f) : f(f) {} ~_deferred() { f(); } private: _deferred(const _deferred&); // don't copy _deferred(_deferred&&); // don't move }; // golang::time:: namespace time { // sleep pauses current goroutine for at least dt seconds. LIBGOLANG_API void sleep(double dt); // now returns current time in seconds. LIBGOLANG_API double now(); } // golang::time:: // golang::sync:: namespace sync { // Sema provides semaphore. class Sema { _sema *_gsema; public: LIBGOLANG_API Sema(); LIBGOLANG_API ~Sema(); LIBGOLANG_API void acquire(); LIBGOLANG_API void release(); private: Sema(const Sema&); // don't copy Sema(Sema&&); // don't move }; // Mutex provides mutex. class Mutex { Sema _sema; public: LIBGOLANG_API Mutex(); LIBGOLANG_API ~Mutex(); LIBGOLANG_API void lock(); LIBGOLANG_API void unlock(); private: Mutex(const Mutex&); // don't copy Mutex(Mutex&&); // don't move }; // Once allows to execute an action only once. // // For example: // // sync::Once once; // ... // once.do_(doSomething); class Once { Mutex _mu; bool _done; public: LIBGOLANG_API Once(); LIBGOLANG_API ~Once(); LIBGOLANG_API void do_(const std::function<void(void)> &f); private: Once(const Once&); // don't copy Once(Once&&); // don't move }; } // golang::sync:: } // golang:: #endif // __cplusplus #endif // _NXD_LIBGOLANG_H