Commit 1a9dae3b authored by Kirill Smelkov's avatar Kirill Smelkov

libgolang: Split time and sync into their own packages

We already have some sync functionality implemented in C++ (e.g.
sync.Once, sync.WaitGroup) and we are going to add more and also move
timers implementation to C++. It is getting crowded for that
functionality to still live in libgolang.{h,cpp}

-> Split sync & time functionality into their own C++ packages (still
built-into libgolang.so)
parent 419c8950
include COPYING README.rst CHANGELOG.rst tox.ini pyproject.toml trun
include golang/libgolang.h
include golang/runtime/libgolang.cpp
include golang/sync.h
include golang/sync.cpp
include golang/time.h
include golang/time.cpp
recursive-include golang *.py *.pxd *.pyx *.toml *.txt
recursive-include gpython *.py
recursive-include 3rdparty *.h
......@@ -26,7 +26,7 @@
See also https://golang.org/pkg/sync for Go sync package documentation.
"""
cdef extern from "golang/libgolang.h" namespace "golang::sync" nogil:
cdef extern from "golang/sync.h" namespace "golang::sync" nogil:
cppclass Sema:
Sema()
void acquire()
......
......@@ -30,17 +30,18 @@ from golang cimport go, chan, makechan, structZ, nil, panic, topyexc
from golang cimport sync, time
# C-level _sema + _mutex
# (not exposed in golang.pxd as it exposes only high-level API)
cdef extern from "golang/libgolang.h" namespace "golang" nogil:
# (not exposed in sync.pxd as it exposes only high-level API)
cdef extern from "golang/sync.h" namespace "golang::sync" nogil:
"""
namespace golang {
namespace sync {
void _mutex_init(sync::Mutex *mu) {
new (mu) sync::Mutex();
}
void _mutex_destroy(sync::Mutex *mu) {
mu->~Mutex();
}
} // golang::
}} // golang::sync::
"""
struct _sema
_sema *_makesema()
......
......@@ -50,6 +50,6 @@ cdef extern from * nogil:
const double hour "golang_time_hour"
cdef extern from "golang/libgolang.h" namespace "golang::time" nogil:
cdef extern from "golang/time.h" namespace "golang::time" nogil:
void sleep(double dt)
double now()
......@@ -39,8 +39,6 @@
// - `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:
//
......@@ -72,8 +70,6 @@
// - `_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
......@@ -91,6 +87,12 @@
// are possible.
//
//
// Additional packages
//
// Libgolang, besides goroutines and channels, also provide additional packages
// that mirror Go analogs. See for example golang/time.h, golang/sync.h, etc.
//
//
// [1] Libtask: a Coroutine Library for C and Unix. https://swtch.com/libtask.
// [2] http://9p.io/magic/man2html/2/thread.
......@@ -129,8 +131,6 @@ 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);
......@@ -231,14 +231,6 @@ _selcase _selrecv_(_chan *ch, void *prx, bool *pok) {
// _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;
......@@ -464,93 +456,6 @@ private:
};
// 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
};
// WaitGroup allows to wait for collection of tasks to finish.
class WaitGroup {
Mutex _mu;
int _count;
chan<structZ> _done; // closed & recreated every time ._count drops to 0
public:
LIBGOLANG_API WaitGroup();
LIBGOLANG_API ~WaitGroup();
LIBGOLANG_API void done();
LIBGOLANG_API void add(int delta);
LIBGOLANG_API void wait();
private:
WaitGroup(const WaitGroup&); // don't copy
WaitGroup(WaitGroup&&); // don't move
};
} // golang::sync::
} // golang::
#endif // __cplusplus
......
......@@ -166,8 +166,10 @@ def Extension(name, sources, **kw):
dependv.append('%s/golang/libgolang.h' % pygo)
dependv.append('%s/golang/_golang.pxd' % pygo)
dependv.append('%s/golang/__init__.pxd' % pygo)
dependv.append('%s/golang/sync.h' % pygo)
dependv.append('%s/golang/sync.pxd' % pygo)
dependv.append('%s/golang/_sync.pxd' % pygo)
dependv.append('%s/golang/time.h' % pygo)
dependv.append('%s/golang/time.pxd' % pygo)
dependv.append('%s/golang/_time.pxd' % pygo)
kw['depends'] = dependv
......
......@@ -28,6 +28,8 @@
// - because Cython (currently ?) does not allow to add methods to `cdef struct`.
#include "golang/libgolang.h"
#include "golang/sync.h"
#include "golang/time.h"
#include <algorithm>
#include <atomic>
......@@ -123,18 +125,13 @@ void _taskgo(void (*f)(void *), void *arg) {
_runtime->go(f, arg);
}
void _tasknanosleep(uint64_t dt) {
_runtime->nanosleep(dt);
}
uint64_t _nanotime() {
return _runtime->nanotime();
}
// ---- semaphores ----
// (_sema = _libgolang_sema)
// golang::sync:: (only Sema and Mutex)
namespace sync {
// _makesema creates new semaphore.
//
// it always returns !NULL and panics on memory allocation failue.
......@@ -157,8 +154,6 @@ void _semarelease(_sema *sema) {
_runtime->sema_release((_libgolang_sema *)sema);
}
// golang::sync:: (only Sema and Mutex)
namespace sync {
Sema::Sema() {
Sema *sema = this;
......@@ -1214,11 +1209,19 @@ int _tchansendqlen(_chan *_ch) {
} // golang::
// ---- golang::time:: ----
// ---- golang::time:: (only sleep and now) ----
namespace golang {
namespace time {
void _tasknanosleep(uint64_t dt) {
_runtime->nanosleep(dt);
}
uint64_t _nanotime() {
return _runtime->nanotime();
}
void sleep(double dt) {
if (dt <= 0)
dt = 0;
......@@ -1238,83 +1241,6 @@ double now() {
}} // golang::time::
// ---- golang::sync:: (except Sema and Mutex) ----
namespace golang {
namespace sync {
// Once
Once::Once() {
Once *once = this;
once->_done = false;
}
Once::~Once() {}
void Once::do_(const std::function<void(void)> &f) {
Once *once = this;
once->_mu.lock();
defer([&]() {
once->_mu.unlock();
});
if (!once->_done) {
once->_done = true;
f();
}
}
// WaitGroup
WaitGroup::WaitGroup() {
WaitGroup& wg = *this;
wg._count = 0;
wg._done = makechan<structZ>();
}
WaitGroup::~WaitGroup() {}
void WaitGroup::done() {
WaitGroup& wg = *this;
wg.add(-1);
}
void WaitGroup::add(int delta) {
WaitGroup& wg = *this;
if (delta == 0)
return;
wg._mu.lock();
defer([&]() {
wg._mu.unlock();
});
wg._count += delta;
if (wg._count < 0)
panic("sync: negative WaitGroup counter");
if (wg._count == 0) {
wg._done.close();
wg._done = makechan<structZ>();
}
}
void WaitGroup::wait() {
WaitGroup& wg = *this;
chan<structZ> done = NULL;
wg._mu.lock();
if (wg._count != 0)
done = wg._done;
wg._mu.unlock();
if (done == NULL) // wg._count was =0
return;
done.recv();
}
}} // golang::sync::
// ---- misc ----
// zalloc allocates zeroed memory.
......
......@@ -20,6 +20,9 @@
// Test that exercises C++-level libgolang.h API and functionality.
#include "golang/libgolang.h"
#include "golang/sync.h"
#include "golang/time.h"
#include <stdio.h>
#include <tuple>
#include <utility>
......
// 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.
// Package sync mirrors Go package sync.
// See sync.h for package overview.
#include "golang/sync.h"
// golang::sync:: (except Sema and Mutex)
namespace golang {
namespace sync {
// Once
Once::Once() {
Once *once = this;
once->_done = false;
}
Once::~Once() {}
void Once::do_(const std::function<void(void)> &f) {
Once *once = this;
once->_mu.lock();
defer([&]() {
once->_mu.unlock();
});
if (!once->_done) {
once->_done = true;
f();
}
}
// WaitGroup
WaitGroup::WaitGroup() {
WaitGroup& wg = *this;
wg._count = 0;
wg._done = makechan<structZ>();
}
WaitGroup::~WaitGroup() {}
void WaitGroup::done() {
WaitGroup& wg = *this;
wg.add(-1);
}
void WaitGroup::add(int delta) {
WaitGroup& wg = *this;
if (delta == 0)
return;
wg._mu.lock();
defer([&]() {
wg._mu.unlock();
});
wg._count += delta;
if (wg._count < 0)
panic("sync: negative WaitGroup counter");
if (wg._count == 0) {
wg._done.close();
wg._done = makechan<structZ>();
}
}
void WaitGroup::wait() {
WaitGroup& wg = *this;
chan<structZ> done = NULL;
wg._mu.lock();
if (wg._count != 0)
done = wg._done;
wg._mu.unlock();
if (done == NULL) // wg._count was =0
return;
done.recv();
}
}} // golang::sync::
#ifndef _NXD_LIBGOLANG_SYNC_H
#define _NXD_LIBGOLANG_SYNC_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.
// Package sync mirrors Go package sync.
//
// - `Once` allows to execute an action only once.
// - `WaitGroup` allows to wait for a collection of tasks to finish.
// - `Sema` and `Mutex` provide low-level synchronization.
//
// See also https://golang.org/pkg/sync for Go sync package documentation.
//
//
// C-level API
//
// Subset of sync package functionality is also provided via C-level API:
//
// - `_makesema` and `_sema*` provide semaphore functionality.
#include <golang/libgolang.h>
// ---- C-level API ----
#ifdef __cplusplus
namespace golang {
namespace sync {
extern "C" {
#endif
// _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);
#ifdef __cplusplus
}}} // golang::sync:: "C"
#endif
// ---- C++ API ----
#ifdef __cplusplus
// golang::sync::
namespace golang {
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
};
// WaitGroup allows to wait for collection of tasks to finish.
class WaitGroup {
Mutex _mu;
int _count;
chan<structZ> _done; // closed & recreated every time ._count drops to 0
public:
LIBGOLANG_API WaitGroup();
LIBGOLANG_API ~WaitGroup();
LIBGOLANG_API void done();
LIBGOLANG_API void add(int delta);
LIBGOLANG_API void wait();
private:
WaitGroup(const WaitGroup&); // don't copy
WaitGroup(WaitGroup&&); // don't move
};
}} // golang::sync::
#endif // __cplusplus
#endif // _NXD_LIBGOLANG_SYNC_H
// Copyright (C) 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.
// Package time mirrors Go package time.
// See time.h for package overview.
#include "golang/time.h"
// golang::time:: (except sleep and now)
namespace golang {
namespace time {
}} // golang::time::
#ifndef _NXD_LIBGOLANG_TIME_H
#define _NXD_LIBGOLANG_TIME_H
// Copyright (C) 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.
// Package time mirrors Go package time.
//
// - `now` returns current time.
// - `sleep` pauses current task.
//
// See also https://golang.org/pkg/time for Go time package documentation.
//
//
// C-level API
//
// Subset of time package functionality is also provided via C-level API:
//
// - `_tasknanosleep` pauses current task.
// - `_nanotime` returns current time.
#include <golang/libgolang.h>
// ---- C-level API ----
#ifdef __cplusplus
namespace golang {
namespace time {
extern "C" {
#endif
LIBGOLANG_API void _tasknanosleep(uint64_t dt);
LIBGOLANG_API uint64_t _nanotime(void);
#ifdef __cplusplus
}}} // golang::time:: "C"
#endif
// ---- C++ API ----
#ifdef __cplusplus
// golang::time::
namespace golang {
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::
#endif // __cplusplus
#endif // _NXD_LIBGOLANG_TIME_H
......@@ -191,8 +191,14 @@ setup(
packages = find_packages(),
x_dsos = [DSO('golang.runtime.libgolang', ['golang/runtime/libgolang.cpp'],
depends = ['golang/libgolang.h'],
x_dsos = [DSO('golang.runtime.libgolang',
['golang/runtime/libgolang.cpp',
'golang/sync.cpp',
'golang/time.cpp'],
depends = [
'golang/libgolang.h',
'golang/sync.h',
'golang/time.h'],
include_dirs = ['.', '3rdparty/include'],
define_macros = [('BUILDING_LIBGOLANG', None)],
extra_compile_args = ['-std=gnu++11'], # not c++11 as linux/list.h uses typeof
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment