Commit ce8152a2 authored by Kirill Smelkov's avatar Kirill Smelkov

pyx api: Provide sleep

- Add sleep functionality to libgolang runtime;
- Implement sleep for thread and gevent runtimes. Thread runtime
  implements sleep independently of GIL, but only for POSIX for now;
- Switch golang.time py module into using golang.time pyx module.

As we are adding sleep, related functionality to query system about
"what is current time?" is also added.
parent ad00be70
/_golang.cpp /_golang.cpp
/_time.cpp
# cython: language_level=2
# 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 the following link about Go time package:
https://golang.org/pkg/time
"""
# golang/pyx - the same as std python - represents time as float
cdef extern from * nogil:
# XXX how to declare/share constants without C verbatim?
"""
#ifndef _golang_time_pxd_h
#define _golang_time_pxd_h
# define golang_time_second (1.0)
# define golang_time_nanosecond (1E-9 * golang_time_second)
# define golang_time_microsecond (1E-6 * golang_time_second)
# define golang_time_millisecond (1E-3 * golang_time_second)
# define golang_time_minute (60 * golang_time_second)
# define golang_time_hour (60 * golang_time_minute)
#endif // _golang_time_pxd_h
"""
const double second "golang_time_second"
const double nanosecond "golang_time_nanosecond"
const double microsecond "golang_time_microsecond"
const double millisecond "golang_time_millisecond"
const double minute "golang_time_minute"
const double hour "golang_time_hour"
cdef extern from "golang/libgolang.h" namespace "golang::time" nogil:
void sleep(double dt)
double now()
# cython: language_level=2
# 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.
"""_time.pyx implements time.pyx - see _time.pxd for package overview."""
def pynow(): # -> t
return now_pyexc()
def pysleep(double dt):
with nogil:
sleep_pyexc(dt)
# ---- misc ----
pysecond = second
pynanosecond = nanosecond
pymicrosecond = microsecond
pymillisecond = millisecond
pyminute = minute
pyhour = hour
from golang cimport topyexc
cdef double now_pyexc() nogil except +topyexc:
return now()
cdef void sleep_pyexc(double dt) nogil except +topyexc:
sleep(dt)
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
// //
// C++-level API // C++-level API
// //
// - `sleep` pauses current task.
// - `panic` throws exception that represent C-level panic. // - `panic` throws exception that represent C-level panic.
// //
// For example: // For example:
...@@ -44,6 +45,9 @@ ...@@ -44,6 +45,9 @@
// //
// C-level API // C-level API
// //
// - `tasknanosleep` pauses current task.
//
//
// Runtimes // Runtimes
// //
// Libgolang, before being used, must be initialized with particular runtime // Libgolang, before being used, must be initialized with particular runtime
...@@ -92,9 +96,19 @@ extern "C" { ...@@ -92,9 +96,19 @@ extern "C" {
LIBGOLANG_API void panic(const char *arg); LIBGOLANG_API void panic(const char *arg);
LIBGOLANG_API const char *recover(void); LIBGOLANG_API const char *recover(void);
LIBGOLANG_API void _tasknanosleep(uint64_t dt);
LIBGOLANG_API uint64_t _nanotime(void);
// 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_runtime_ops { typedef struct _libgolang_runtime_ops {
// 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_runtime_ops;
LIBGOLANG_API void _libgolang_init(const _libgolang_runtime_ops *runtime_ops); LIBGOLANG_API void _libgolang_init(const _libgolang_runtime_ops *runtime_ops);
...@@ -104,4 +118,24 @@ LIBGOLANG_API void _libgolang_init(const _libgolang_runtime_ops *runtime_ops); ...@@ -104,4 +118,24 @@ LIBGOLANG_API void _libgolang_init(const _libgolang_runtime_ops *runtime_ops);
}} }}
#endif #endif
// ---- C++-level API that is available when compiling with C++ ----
#ifdef __cplusplus
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::
} // golang::
#endif // __cplusplus
#endif // _NXD_LIBGOLANG_H #endif // _NXD_LIBGOLANG_H
...@@ -120,5 +120,17 @@ def Extension(name, sources, **kw): ...@@ -120,5 +120,17 @@ def Extension(name, sources, **kw):
if exists(venv_inc): if exists(venv_inc):
kw['include_dirs'].append(venv_inc) kw['include_dirs'].append(venv_inc)
# provide POSIX/... defines to Cython
POSIX = ('posix' in sys.builtin_module_names)
pyxenv = kw.get('cython_compile_time_env', {})
pyxenv.setdefault('POSIX', POSIX)
kw['cython_compile_time_env'] = pyxenv
# XXX hack, because setuptools_dso.Extension is not Cython.Extension
# del from kw to avoid "Unknown Extension options: 'cython_compile_time_env'"
#ext = setuptools_dso.Extension(name, sources, **kw)
pyxenv = kw.pop('cython_compile_time_env')
ext = setuptools_dso.Extension(name, sources, **kw) ext = setuptools_dso.Extension(name, sources, **kw)
ext.cython_compile_time_env = pyxenv
return ext return ext
...@@ -19,6 +19,13 @@ ...@@ -19,6 +19,13 @@
# See https://www.nexedi.com/licensing for rationale and options. # See https://www.nexedi.com/licensing for rationale and options.
"""pyx declarations for libgolang bits that are only interesting for runtimes.""" """pyx declarations for libgolang bits that are only interesting for runtimes."""
from libc.stdint cimport uint64_t
cdef extern from "golang/libgolang.h" nogil: cdef extern from "golang/libgolang.h" nogil:
struct _libgolang_runtime_ops: struct _libgolang_runtime_ops:
pass void (*nanosleep)(uint64_t)
uint64_t (*nanotime)()
# XXX better take from golang.pxd, but there it is declared in `namespace
# "golang"` which fails for C-mode compiles.
void panic(const char *)
...@@ -21,12 +21,30 @@ ...@@ -21,12 +21,30 @@
from __future__ import print_function, absolute_import from __future__ import print_function, absolute_import
from golang.runtime._libgolang cimport _libgolang_runtime_ops from gevent import sleep as pygsleep
from libc.stdint cimport uint64_t
from golang.runtime._libgolang cimport _libgolang_runtime_ops, panic
from golang.runtime cimport _runtime_thread
cdef nogil: cdef nogil:
# XXX better panic with pyexc object and detect that at recover side?
bint _nanosleep(uint64_t dt):
cdef double dt_s = dt * 1E-9
with gil:
pygsleep(dt_s)
return True
void nanosleep(uint64_t dt):
ok = _nanosleep(dt)
if not ok:
panic("pyxgo: gevent: sleep: failed")
# XXX const # XXX const
_libgolang_runtime_ops gevent_ops = _libgolang_runtime_ops( _libgolang_runtime_ops gevent_ops = _libgolang_runtime_ops(
nanosleep = nanosleep,
nanotime = _runtime_thread.nanotime, # reuse from _runtime_thread
) )
from cpython cimport PyCapsule_New from cpython cimport PyCapsule_New
......
# cython: language_level=2
# 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.
from libc.stdint cimport uint64_t
cdef nogil:
# _runtime_gevent reuses thread's nanotime
uint64_t nanotime()
...@@ -21,12 +21,73 @@ ...@@ -21,12 +21,73 @@
from __future__ import print_function, absolute_import from __future__ import print_function, absolute_import
from golang.runtime._libgolang cimport _libgolang_runtime_ops from golang.runtime._libgolang cimport _libgolang_runtime_ops, panic
from libc.stdint cimport uint64_t, UINT64_MAX
IF POSIX:
from posix.time cimport clock_gettime, nanosleep as posix_nanosleep, timespec, CLOCK_REALTIME
from libc.errno cimport errno, EINTR
ELSE:
# for !posix timing fallback
import time as pytimemod
DEF i1E9 = 1000000000
# 987654321
cdef nogil: cdef nogil:
IF POSIX:
void nanosleep(uint64_t dt):
cdef timespec ts
ts.tv_sec = dt // i1E9
ts.tv_nsec = dt % i1E9
err = posix_nanosleep(&ts, NULL)
if err == -1 and errno == EINTR:
err = 0 # XXX ok?
if err == -1:
panic("pyxgo: thread: nanosleep: nanosleep failed") # XXX +errno
ELSE:
bint _nanosleep(uint64_t dt):
cdef double dt_s = dt * 1E-9 # no overflow possible
with gil:
pytimemod.sleep(dt_s)
return True
void nanosleep(uint64_t dt):
ok = _nanosleep(dt)
if not ok:
panic("pyxgo: thread: nanosleep: pytime.sleep failed")
IF POSIX:
uint64_t nanotime():
cdef timespec ts
cdef int err = clock_gettime(CLOCK_REALTIME, &ts)
if err == -1:
panic("pyxgo: thread: nanotime: clock_gettime failed") # XXX +errno
if not (0 <= ts.tv_sec and (0 <= ts.tv_nsec <= i1E9)):
panic("pyxgo: thread: nanotime: clock_gettime -> invalid")
if ts.tv_sec > (UINT64_MAX / i1E9 - 1):
panic("pyxgo: thread: nanotime: clock_gettime -> overflow")
return ts.tv_sec*i1E9 + ts.tv_nsec
ELSE:
(uint64_t, bint) _nanotime():
cdef double t_s
with gil:
t_s = pytimemod.time()
t_ns = t_s * 1E9
if t_ns > UINT64_MAX:
panic("pyxgo: thread: nanotime: time overflow")
return <uint64_t>t_ns, True
uint64_t nanotime():
t, ok = _nanotime()
if not ok:
panic("pyxgo: thread: nanotime: pytime.time failed")
return t
# XXX const # XXX const
_libgolang_runtime_ops thread_ops = _libgolang_runtime_ops( _libgolang_runtime_ops thread_ops = _libgolang_runtime_ops(
nanosleep = nanosleep,
nanotime = nanotime,
) )
from cpython cimport PyCapsule_New from cpython cimport PyCapsule_New
......
...@@ -28,9 +28,11 @@ ...@@ -28,9 +28,11 @@
#include "golang/libgolang.h" #include "golang/libgolang.h"
#include <exception> #include <exception>
#include <limits>
#include <string> #include <string>
using std::exception; using std::exception;
using std::numeric_limits;
using std::string; using std::string;
namespace golang { namespace golang {
...@@ -89,4 +91,36 @@ void _libgolang_init(const _libgolang_runtime_ops *runtime_ops) { ...@@ -89,4 +91,36 @@ void _libgolang_init(const _libgolang_runtime_ops *runtime_ops) {
_runtime = runtime_ops; _runtime = runtime_ops;
} }
void _tasknanosleep(uint64_t dt) {
_runtime->nanosleep(dt);
}
uint64_t _nanotime() {
return _runtime->nanotime();
}
} // golang:: } // golang::
// ---- golang::time:: ----
namespace golang {
namespace time {
void sleep(double dt) {
if (dt <= 0)
dt = 0;
dt *= 1E9; // s -> ns
if (dt > numeric_limits<uint64_t>::max())
panic("sleep: dt overflow");
uint64_t dt_ns = dt;
_tasknanosleep(dt_ns);
}
double now() {
uint64_t t_ns = _nanotime();
double t_s = t_ns * 1E-9; // no overflow possible
return t_s;
}
}} // golang::time::
# cython: language_level=2
# 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.pxd for package documentation.
"""
# redirect cimport: golang.time -> golang._time (see __init__.pxd for rationale)
from golang._time cimport *
...@@ -26,20 +26,19 @@ See the following link about Go time package: ...@@ -26,20 +26,19 @@ See the following link about Go time package:
from __future__ import print_function, absolute_import from __future__ import print_function, absolute_import
import time as _time
from golang import go, chan, select, default, nilchan, panic from golang import go, chan, select, default, nilchan, panic
import threading import threading
# golang/py - the same as std python - represents time as float from golang._time import \
second = 1.0 pysecond as second, \
nanosecond = 1E-9 * second pynanosecond as nanosecond, \
microsecond = 1E-6 * second pymicrosecond as microsecond, \
millisecond = 1E-3 * second pymillisecond as millisecond, \
minute = 60 * second pyminute as minute, \
hour = 60 * minute pyhour as hour, \
\
sleep = _time.sleep pynow as now, \
now = _time.time pysleep as sleep \
# ---- timers ---- # ---- timers ----
......
...@@ -207,6 +207,9 @@ setup( ...@@ -207,6 +207,9 @@ setup(
Ext('golang.runtime._runtime_gevent', Ext('golang.runtime._runtime_gevent',
['golang/runtime/_runtime_gevent.pyx'], ['golang/runtime/_runtime_gevent.pyx'],
language = 'c'), language = 'c'),
Ext('golang._time',
['golang/_time.pyx']),
], ],
include_package_data = True, include_package_data = True,
......
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