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 COPYING README.rst CHANGELOG.rst tox.ini pyproject.toml trun
include golang/libgolang.h include golang/libgolang.h
include golang/runtime/libgolang.cpp 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 golang *.py *.pxd *.pyx *.toml *.txt
recursive-include gpython *.py recursive-include gpython *.py
recursive-include 3rdparty *.h recursive-include 3rdparty *.h
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
See also https://golang.org/pkg/sync for Go sync package documentation. 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: cppclass Sema:
Sema() Sema()
void acquire() void acquire()
......
...@@ -30,17 +30,18 @@ from golang cimport go, chan, makechan, structZ, nil, panic, topyexc ...@@ -30,17 +30,18 @@ from golang cimport go, chan, makechan, structZ, nil, panic, topyexc
from golang cimport sync, time from golang cimport sync, time
# C-level _sema + _mutex # C-level _sema + _mutex
# (not exposed in golang.pxd as it exposes only high-level API) # (not exposed in sync.pxd as it exposes only high-level API)
cdef extern from "golang/libgolang.h" namespace "golang" nogil: cdef extern from "golang/sync.h" namespace "golang::sync" nogil:
""" """
namespace golang { namespace golang {
namespace sync {
void _mutex_init(sync::Mutex *mu) { void _mutex_init(sync::Mutex *mu) {
new (mu) sync::Mutex(); new (mu) sync::Mutex();
} }
void _mutex_destroy(sync::Mutex *mu) { void _mutex_destroy(sync::Mutex *mu) {
mu->~Mutex(); mu->~Mutex();
} }
} // golang:: }} // golang::sync::
""" """
struct _sema struct _sema
_sema *_makesema() _sema *_makesema()
......
...@@ -50,6 +50,6 @@ cdef extern from * nogil: ...@@ -50,6 +50,6 @@ cdef extern from * nogil:
const double hour "golang_time_hour" 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) void sleep(double dt)
double now() double now()
...@@ -39,8 +39,6 @@ ...@@ -39,8 +39,6 @@
// - `chan<T>`, and `select` provide channels with Go semantic and automatic // - `chan<T>`, and `select` provide channels with Go semantic and automatic
// lifetime management. // lifetime management.
// - `panic` throws exception that represent C-level panic. // - `panic` throws exception that represent C-level panic.
// - `time::sleep` pauses current task.
// - `sync::Sema` and `sync::Mutex` provide low-level synchronization.
// //
// For example: // For example:
// //
...@@ -72,8 +70,6 @@ ...@@ -72,8 +70,6 @@
// - `_chanxincref` and `_chanxdecref` manage channel lifetime. // - `_chanxincref` and `_chanxdecref` manage channel lifetime.
// - `_chansend` and `_chanrecv` send/receive over raw channel. // - `_chansend` and `_chanrecv` send/receive over raw channel.
// - `_chanselect`, `_selsend`, `_selrecv`, ... provide raw select functionality. // - `_chanselect`, `_selsend`, `_selrecv`, ... provide raw select functionality.
// - `_tasknanosleep` pauses current task.
// - `_makesema` and `_sema*` provide semaphore functionality.
// //
// //
// Runtimes // Runtimes
...@@ -91,6 +87,12 @@ ...@@ -91,6 +87,12 @@
// are possible. // 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. // [1] Libtask: a Coroutine Library for C and Unix. https://swtch.com/libtask.
// [2] http://9p.io/magic/man2html/2/thread. // [2] http://9p.io/magic/man2html/2/thread.
...@@ -129,8 +131,6 @@ LIBGOLANG_API void panic(const char *arg); ...@@ -129,8 +131,6 @@ LIBGOLANG_API void panic(const char *arg);
LIBGOLANG_API const char *recover(void); LIBGOLANG_API const char *recover(void);
LIBGOLANG_API void _taskgo(void (*f)(void *arg), void *arg); 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; typedef struct _chan _chan;
LIBGOLANG_API _chan *_makechan(unsigned elemsize, unsigned size); LIBGOLANG_API _chan *_makechan(unsigned elemsize, unsigned size);
...@@ -231,14 +231,6 @@ _selcase _selrecv_(_chan *ch, void *prx, bool *pok) { ...@@ -231,14 +231,6 @@ _selcase _selrecv_(_chan *ch, void *prx, bool *pok) {
// _default represents default case for _chanselect. // _default represents default case for _chanselect.
extern LIBGOLANG_API const _selcase _default; 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. // libgolang runtime - the runtime must be initialized before any other libgolang use.
typedef struct _libgolang_sema _libgolang_sema; typedef struct _libgolang_sema _libgolang_sema;
...@@ -464,93 +456,6 @@ private: ...@@ -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:: } // golang::
#endif // __cplusplus #endif // __cplusplus
......
...@@ -166,8 +166,10 @@ def Extension(name, sources, **kw): ...@@ -166,8 +166,10 @@ def Extension(name, sources, **kw):
dependv.append('%s/golang/libgolang.h' % pygo) dependv.append('%s/golang/libgolang.h' % pygo)
dependv.append('%s/golang/_golang.pxd' % pygo) dependv.append('%s/golang/_golang.pxd' % pygo)
dependv.append('%s/golang/__init__.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/_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)
dependv.append('%s/golang/_time.pxd' % pygo) dependv.append('%s/golang/_time.pxd' % pygo)
kw['depends'] = dependv kw['depends'] = dependv
......
...@@ -28,6 +28,8 @@ ...@@ -28,6 +28,8 @@
// - because Cython (currently ?) does not allow to add methods to `cdef struct`. // - because Cython (currently ?) does not allow to add methods to `cdef struct`.
#include "golang/libgolang.h" #include "golang/libgolang.h"
#include "golang/sync.h"
#include "golang/time.h"
#include <algorithm> #include <algorithm>
#include <atomic> #include <atomic>
...@@ -123,18 +125,13 @@ void _taskgo(void (*f)(void *), void *arg) { ...@@ -123,18 +125,13 @@ void _taskgo(void (*f)(void *), void *arg) {
_runtime->go(f, arg); _runtime->go(f, arg);
} }
void _tasknanosleep(uint64_t dt) {
_runtime->nanosleep(dt);
}
uint64_t _nanotime() {
return _runtime->nanotime();
}
// ---- semaphores ---- // ---- semaphores ----
// (_sema = _libgolang_sema) // (_sema = _libgolang_sema)
// golang::sync:: (only Sema and Mutex)
namespace sync {
// _makesema creates new semaphore. // _makesema creates new semaphore.
// //
// it always returns !NULL and panics on memory allocation failue. // it always returns !NULL and panics on memory allocation failue.
...@@ -157,8 +154,6 @@ void _semarelease(_sema *sema) { ...@@ -157,8 +154,6 @@ void _semarelease(_sema *sema) {
_runtime->sema_release((_libgolang_sema *)sema); _runtime->sema_release((_libgolang_sema *)sema);
} }
// golang::sync:: (only Sema and Mutex)
namespace sync {
Sema::Sema() { Sema::Sema() {
Sema *sema = this; Sema *sema = this;
...@@ -1214,11 +1209,19 @@ int _tchansendqlen(_chan *_ch) { ...@@ -1214,11 +1209,19 @@ int _tchansendqlen(_chan *_ch) {
} // golang:: } // golang::
// ---- golang::time:: ---- // ---- golang::time:: (only sleep and now) ----
namespace golang { namespace golang {
namespace time { namespace time {
void _tasknanosleep(uint64_t dt) {
_runtime->nanosleep(dt);
}
uint64_t _nanotime() {
return _runtime->nanotime();
}
void sleep(double dt) { void sleep(double dt) {
if (dt <= 0) if (dt <= 0)
dt = 0; dt = 0;
...@@ -1238,83 +1241,6 @@ double now() { ...@@ -1238,83 +1241,6 @@ double now() {
}} // golang::time:: }} // 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 ---- // ---- misc ----
// zalloc allocates zeroed memory. // zalloc allocates zeroed memory.
......
...@@ -20,6 +20,9 @@ ...@@ -20,6 +20,9 @@
// Test that exercises C++-level libgolang.h API and functionality. // Test that exercises C++-level libgolang.h API and functionality.
#include "golang/libgolang.h" #include "golang/libgolang.h"
#include "golang/sync.h"
#include "golang/time.h"
#include <stdio.h> #include <stdio.h>
#include <tuple> #include <tuple>
#include <utility> #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( ...@@ -191,8 +191,14 @@ setup(
packages = find_packages(), packages = find_packages(),
x_dsos = [DSO('golang.runtime.libgolang', ['golang/runtime/libgolang.cpp'], x_dsos = [DSO('golang.runtime.libgolang',
depends = ['golang/libgolang.h'], ['golang/runtime/libgolang.cpp',
'golang/sync.cpp',
'golang/time.cpp'],
depends = [
'golang/libgolang.h',
'golang/sync.h',
'golang/time.h'],
include_dirs = ['.', '3rdparty/include'], include_dirs = ['.', '3rdparty/include'],
define_macros = [('BUILDING_LIBGOLANG', None)], define_macros = [('BUILDING_LIBGOLANG', None)],
extra_compile_args = ['-std=gnu++11'], # not c++11 as linux/list.h uses typeof 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