Commit d0688e21 authored by Kirill Smelkov's avatar Kirill Smelkov

context: Reorganize the code to make internal logic more clear

- _propagateCancel is used only in _BaseCtx constructor -> inline it
  there. Being run in the constructor makes it clear that this code
  works on new _BaseCtx object with empty set of children.

- since _cancelFrom interacts with the code moved from _propagateCancel,
  also move it to be close to cancel propagation setup.

No functional changes, just plain code movement.

/trusted-by @jerome
/reviewed-on nexedi/pygolang!16
parent 8136e5e9
// Copyright (C) 2019-2020 Nexedi SA and Contributors.
// Copyright (C) 2019-2021 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com>
//
// This program is free software: you can Use, Study, Modify and Redistribute
......@@ -103,52 +103,54 @@ struct _BaseCtx : _Context, object {
panic("BUG: _BaseCtx: done==nil, but len(parentv) != 1");
}
ctx._propagateCancel();
}
// establishes setup so that whenever a parent is canceled,
// ctx and its children are canceled too.
refptr<_BaseCtx> bctx = newref(&ctx);
chan<structZ> done() {
_BaseCtx& ctx = *this;
vector<Context> pforeignv; // parents with !nil .done() for foreign contexts
for (auto parent : ctx._parentv) {
// if parent can never be canceled (e.g. it is background) - we
// don't need to propagate cancel from it.
chan<structZ> pdone = parent->done();
if (pdone == nil)
continue;
if (ctx._done != nil)
return ctx._done;
return ctx._parentv[0]->done();
// parent is cancellable - glue to propagate cancel from it to us
_BaseCtx *_parent = dynamic_cast<_BaseCtx *>(parent._ptr());
if (_parent != nil) {
_parent->_mu.lock();
if (_parent->_err != nil)
ctx._cancel(_parent->_err);
else
_parent->_children.insert(bctx);
_parent->_mu.unlock();
}
else {
if (_ready(pdone))
ctx._cancel(parent->err());
else
pforeignv.push_back(parent);
}
error err() {
_BaseCtx& ctx = *this;
ctx._mu.lock();
defer([&]() {
ctx._mu.unlock();
});
return ctx._err;
}
interface value(const void *key) {
_BaseCtx& ctx = *this;
if (pforeignv.size() == 0)
return;
for (auto parent : ctx._parentv) {
interface v = parent->value(key);
if (v != nil)
return v;
}
return nil;
}
// there are some foreign contexts to propagate cancel from
go([bctx,pforeignv]() {
vector<_selcase> sel(1+pforeignv.size());
sel[0] = bctx->_done.recvs(); // 0
for (size_t i=0; i<pforeignv.size(); i++)
sel[1+i] = pforeignv[i]->done().recvs(); // 1 + ...
double deadline() {
_BaseCtx& ctx = *this;
int _ = select(sel);
double d = INFINITY;
for (auto parent : ctx._parentv) {
double pd = parent->deadline();
if (pd < d)
d = pd;
}
return d;
// 0. nothing - already canceled
if (_ > 0)
bctx->_cancel(pforeignv[_-1]->err());
});
}
// _cancel cancels ctx and its children.
void _cancel(error err) {
_BaseCtx& ctx = *this;
......@@ -193,54 +195,47 @@ struct _BaseCtx : _Context, object {
child->_cancelFrom(cctx, err);
}
// _propagateCancel establishes setup so that whenever a parent is canceled,
// ctx and its children are canceled too.
void _propagateCancel() {
_BaseCtx& ctx = *this;
refptr<_BaseCtx> bctx = newref(&ctx);
vector<Context> pforeignv; // parents with !nil .done() for foreign contexts
for (auto parent : ctx._parentv) {
// if parent can never be canceled (e.g. it is background) - we
// don't need to propagate cancel from it.
chan<structZ> pdone = parent->done();
if (pdone == nil)
continue;
chan<structZ> done() {
_BaseCtx& ctx = *this;
// parent is cancellable - glue to propagate cancel from it to us
_BaseCtx *_parent = dynamic_cast<_BaseCtx *>(parent._ptr());
if (_parent != nil) {
_parent->_mu.lock();
if (_parent->_err != nil)
ctx._cancel(_parent->_err);
else
_parent->_children.insert(bctx);
_parent->_mu.unlock();
}
else {
if (_ready(pdone))
ctx._cancel(parent->err());
else
pforeignv.push_back(parent);
if (ctx._done != nil)
return ctx._done;
return ctx._parentv[0]->done();
}
error err() {
_BaseCtx& ctx = *this;
ctx._mu.lock();
defer([&]() {
ctx._mu.unlock();
});
return ctx._err;
}
if (pforeignv.size() == 0)
return;
interface value(const void *key) {
_BaseCtx& ctx = *this;
// there are some foreign contexts to propagate cancel from
go([bctx,pforeignv]() {
vector<_selcase> sel(1+pforeignv.size());
sel[0] = bctx->_done.recvs(); // 0
for (size_t i=0; i<pforeignv.size(); i++)
sel[1+i] = pforeignv[i]->done().recvs(); // 1 + ...
for (auto parent : ctx._parentv) {
interface v = parent->value(key);
if (v != nil)
return v;
}
return nil;
}
int _ = select(sel);
double deadline() {
_BaseCtx& ctx = *this;
// 0. nothing - already canceled
if (_ > 0)
bctx->_cancel(pforeignv[_-1]->err());
});
double d = INFINITY;
for (auto parent : ctx._parentv) {
double pd = parent->deadline();
if (pd < d)
d = pd;
}
return d;
}
};
......
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