Commit 2a359791 authored by Kirill Smelkov's avatar Kirill Smelkov

context: Move/Port context package to C++/Pyx nogil

Provide context-related functionality that can be used directly from C++
and Pyx/nogil codes. Python-level classes and functions become small
wrappers around pyx/nogil ones.

Like with timers (b073f6df "time: Move/Port timers to C++/Pyx nogil")
and interfaces (5a99b769 "libgolang: Start providing interfaces")
memory for objects dynamically allocated on heap is managed
automatically.
parent 9785f2d3
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/context.h
include golang/context.cpp
include golang/cxx.h include golang/cxx.h
include golang/errors.h include golang/errors.h
include golang/errors.cpp include golang/errors.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 context mirrors and amends Go package context.
- `Context` represents operational context that carries deadline, cancellation
signal and immutable context-local key -> value dict.
- `background` returns empty context that is never canceled.
- `with_cancel` creates new context that can be canceled on its own.
- `with_deadline` creates new context with deadline.
- `with_timeout` creates new context with timeout.
- `with_value` creates new context with attached key=value.
- `merge` creates new context from 2 parents(*).
See also https://golang.org/pkg/context for Go context package documentation.
See also https://blog.golang.org/context for overview.
(*) not provided in Go version.
"""
from golang cimport chan, structZ, error, refptr, interface
from golang cimport cxx
from libcpp.utility cimport pair
# XXX for std::function cython does not provide operator() and =nullptr
#from libcpp.functional cimport function
#ctypedef function[void()] cancelFunc
cdef extern from "<functional>" namespace "std" nogil:
cppclass cancelFunc "std::function<void()>":
void operator() ()
void operator= (nullptr_t)
cdef extern from "golang/context.h" namespace "golang::context" nogil:
cppclass _Context:
double deadline()
chan[structZ] done()
error err()
interface value(const void *key)
cppclass Context (refptr[_Context]):
# Context.X = Context->X in C++
double deadline "_ptr()->deadline" ()
chan[structZ] done "_ptr()->done" ()
error err "_ptr()->err" ()
interface value "_ptr()->value" (const void *key)
Context background()
error canceled
error deadlineExceeded
pair[Context, cancelFunc] with_cancel (Context parent)
Context with_value (Context parent, const void *key, interface value)
pair[Context, cancelFunc] with_deadline (Context parent, double deadline)
pair[Context, cancelFunc] with_timeout (Context parent, double timeout)
pair[Context, cancelFunc] merge (Context parent1, Context parent2)
# for testing
cxx.set[Context] _tctxchildren(Context ctx)
This diff is collapsed.
This diff is collapsed.
#ifndef _NXD_LIBGOLANG_CONTEXT_H
#define _NXD_LIBGOLANG_CONTEXT_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 context mirrors and amends Go package context.
//
// - `Context` represents operational context that carries deadline, cancellation
// signal and immutable context-local key -> value dict.
// - `background` returns empty context that is never canceled.
// - `with_cancel` creates new context that can be canceled on its own.
// - `with_deadline` creates new context with deadline.
// - `with_timeout` creates new context with timeout.
// - `with_value` creates new context with attached key=value.
// - `merge` creates new context from 2 parents(*).
//
// See also https://golang.org/pkg/context for Go context package documentation.
// See also https://blog.golang.org/context for overview.
//
// (*) not provided in Go version.
#include <golang/libgolang.h>
#include <golang/cxx.h>
// golang::context::
namespace golang {
namespace context {
// Context is the interface that every context must implement.
//
// A context carries deadline, cancellation signal and immutable context-local
// key -> value dict.
struct _Context : _interface {
// deadline() returns context deadline or +inf, if there is no deadline.
virtual double deadline() = 0; // -> time | INFINITY
// done returns channel that is closed when the context is canceled.
virtual chan<structZ> done() = 0;
// err returns nil if done is not yet closed, or error that explains why context was canceled.
virtual error err() = 0;
// value returns value associated with key, or nil, if context has no key.
virtual interface value(const void *key) = 0; // -> value | nil
};
typedef refptr<_Context> Context;
// background returns empty context that is never canceled.
LIBGOLANG_API Context background();
// canceled is the error returned by Context.err when context is canceled.
extern LIBGOLANG_API const error canceled;
// deadlineExceeded is the error returned by Context.err when time goes past context's deadline.
extern LIBGOLANG_API const error deadlineExceeded;
// with_cancel creates new context that can be canceled on its own.
//
// Returned context inherits from parent and in particular is canceled when
// parent is done.
//
// The caller should explicitly call cancel to release context resources as soon
// the context is no longer needed.
LIBGOLANG_API std::pair<Context, std::function<void()>>
with_cancel(Context parent); // -> ctx, cancel
// with_value creates new context with key=value.
//
// Returned context inherits from parent and in particular has all other
// (key, value) pairs provided by parent.
LIBGOLANG_API Context
with_value(Context parent, const void *key, interface value); // -> ctx
// with_deadline creates new context with deadline.
//
// The deadline of created context is the earliest of provided deadline or
// deadline of parent. Created context will be canceled when time goes past
// context deadline or cancel called, whichever happens first.
//
// The caller should explicitly call cancel to release context resources as soon
// the context is no longer needed.
LIBGOLANG_API std::pair<Context, std::function<void()>>
with_deadline(Context parent, double deadline); // -> ctx, cancel
// with_timeout creates new context with timeout.
//
// it is shorthand for with_deadline(parent, now+timeout).
LIBGOLANG_API std::pair<Context, std::function<void()>>
with_timeout(Context parent, double timeout); // -> ctx, cancel
// merge merges 2 contexts into 1.
//
// The result context:
//
// - is done when parent1 or parent2 is done, or cancel called, whichever happens first,
// - has deadline = min(parent1.Deadline, parent2.Deadline),
// - has associated values merged from parent1 and parent2, with parent1 taking precedence.
//
// Canceling this context releases resources associated with it, so code should
// call cancel as soon as the operations running in this Context complete.
//
// Note: on Go side merge is not part of stdlib context and is provided by
// https://godoc.org/lab.nexedi.com/kirr/go123/xcontext#hdr-Merging_contexts
LIBGOLANG_API std::pair<Context, std::function<void()>>
merge(Context parent1, Context parent2); // -> ctx, cancel
// for testing
LIBGOLANG_API cxx::set<Context> _tctxchildren(Context ctx);
}} // golang::context::
#endif // _NXD_LIBGOLANG_CONTEXT_H
# 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 context mirrors and amends Go package context.
See _context.pxd for package documentation.
"""
# redirect cimport: golang.context -> golang._context (see __init__.pxd for rationale)
from golang._context cimport *
...@@ -20,18 +20,19 @@ ...@@ -20,18 +20,19 @@
from __future__ import print_function, absolute_import from __future__ import print_function, absolute_import
from golang import context, _context, time, nilchan from golang import nilchan, select, default
from golang._context import _tctxchildren as tctxchildren, _ready as ready from golang import context, _context, time
from golang._context import _tctxAssertChildren as tctxAssertChildren
from golang.time_test import dt from golang.time_test import dt
# assertCtx asserts on state of _BaseCtx* # assertCtx asserts on state of _BaseCtx*
def assertCtx(ctx, children, deadline=None, err=None, done=False): def assertCtx(ctx, children, deadline=None, err=None, done=False):
assert isinstance(ctx, _context._BaseCtx) assert isinstance(ctx, _context.PyContext)
assert ctx.deadline() == deadline assert ctx.deadline() == deadline
assert ctx.err() is err assert ctx.err() is err
ctxdone = ctx.done() ctxdone = ctx.done()
assert ready(ctxdone) == done assert ready(ctxdone) == done
assert tctxchildren(ctx) == children tctxAssertChildren(ctx, children)
for i in range(10): # repeated .done() returns the same pyobject for i in range(10): # repeated .done() returns the same pyobject
assert ctx.done() is ctxdone assert ctx.done() is ctxdone
...@@ -262,3 +263,17 @@ def test_deadline(): ...@@ -262,3 +263,17 @@ def test_deadline():
time.sleep(11*dt) time.sleep(11*dt)
assertCtx(ctx, Z, deadline=d, err=D, done=Y) assertCtx(ctx, Z, deadline=d, err=D, done=Y)
# ---- misc ----
# _ready returns whether channel ch is ready.
def ready(ch):
_, _rx = select(
ch.recv, # 0
default, # 1
)
if _ == 0:
return True
if _ == 1:
return False
...@@ -166,6 +166,9 @@ def Extension(name, sources, **kw): ...@@ -166,6 +166,9 @@ 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/context.h' % pygo)
dependv.append('%s/golang/context.pxd' % pygo)
dependv.append('%s/golang/_context.pxd' % pygo)
dependv.append('%s/golang/cxx.h' % pygo) dependv.append('%s/golang/cxx.h' % pygo)
dependv.append('%s/golang/cxx.pxd' % pygo) dependv.append('%s/golang/cxx.pxd' % pygo)
dependv.append('%s/golang/errors.h' % pygo) dependv.append('%s/golang/errors.h' % pygo)
......
...@@ -193,11 +193,13 @@ setup( ...@@ -193,11 +193,13 @@ setup(
x_dsos = [DSO('golang.runtime.libgolang', x_dsos = [DSO('golang.runtime.libgolang',
['golang/runtime/libgolang.cpp', ['golang/runtime/libgolang.cpp',
'golang/context.cpp',
'golang/errors.cpp', 'golang/errors.cpp',
'golang/sync.cpp', 'golang/sync.cpp',
'golang/time.cpp'], 'golang/time.cpp'],
depends = [ depends = [
'golang/libgolang.h', 'golang/libgolang.h',
'golang/context.h',
'golang/cxx.h', 'golang/cxx.h',
'golang/errors.h', 'golang/errors.h',
'golang/sync.h', 'golang/sync.h',
......
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