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
# 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 @@
//
// C++-level API
//
// - `sleep` pauses current task.
// - `panic` throws exception that represent C-level panic.
//
// For example:
......@@ -44,6 +45,9 @@
//
// C-level API
//
// - `tasknanosleep` pauses current task.
//
//
// Runtimes
//
// Libgolang, before being used, must be initialized with particular runtime
......@@ -92,9 +96,19 @@ extern "C" {
LIBGOLANG_API void panic(const char *arg);
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.
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_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
// ---- 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
......@@ -120,5 +120,17 @@ def Extension(name, sources, **kw):
if exists(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.cython_compile_time_env = pyxenv
return ext
......@@ -19,6 +19,13 @@
# See https://www.nexedi.com/licensing for rationale and options.
"""pyx declarations for libgolang bits that are only interesting for runtimes."""
from libc.stdint cimport uint64_t
cdef extern from "golang/libgolang.h" nogil:
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 @@
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:
# 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
_libgolang_runtime_ops gevent_ops = _libgolang_runtime_ops(
nanosleep = nanosleep,
nanotime = _runtime_thread.nanotime, # reuse from _runtime_thread
)
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 @@
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:
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
_libgolang_runtime_ops thread_ops = _libgolang_runtime_ops(
nanosleep = nanosleep,
nanotime = nanotime,
)
from cpython cimport PyCapsule_New
......
......@@ -28,9 +28,11 @@
#include "golang/libgolang.h"
#include <exception>
#include <limits>
#include <string>
using std::exception;
using std::numeric_limits;
using std::string;
namespace golang {
......@@ -89,4 +91,36 @@ void _libgolang_init(const _libgolang_runtime_ops *runtime_ops) {
_runtime = runtime_ops;
}
void _tasknanosleep(uint64_t dt) {
_runtime->nanosleep(dt);
}
uint64_t _nanotime() {
return _runtime->nanotime();
}
} // 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:
from __future__ import print_function, absolute_import
import time as _time
from golang import go, chan, select, default, nilchan, panic
import threading
# golang/py - the same as std python - represents time as float
second = 1.0
nanosecond = 1E-9 * second
microsecond = 1E-6 * second
millisecond = 1E-3 * second
minute = 60 * second
hour = 60 * minute
sleep = _time.sleep
now = _time.time
from golang._time import \
pysecond as second, \
pynanosecond as nanosecond, \
pymicrosecond as microsecond, \
pymillisecond as millisecond, \
pyminute as minute, \
pyhour as hour, \
\
pynow as now, \
pysleep as sleep \
# ---- timers ----
......
......@@ -207,6 +207,9 @@ setup(
Ext('golang.runtime._runtime_gevent',
['golang/runtime/_runtime_gevent.pyx'],
language = 'c'),
Ext('golang._time',
['golang/_time.pyx']),
],
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