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