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() { vector<Context> pforeignv; // parents with !nil .done() for foreign contexts
_BaseCtx& ctx = *this; 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) // parent is cancellable - glue to propagate cancel from it to us
return ctx._done; _BaseCtx *_parent = dynamic_cast<_BaseCtx *>(parent._ptr());
return ctx._parentv[0]->done(); 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) { 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.
void _propagateCancel() {
_BaseCtx& ctx = *this;
refptr<_BaseCtx> bctx = newref(&ctx);
vector<Context> pforeignv; // parents with !nil .done() for foreign contexts chan<structZ> done() {
for (auto parent : ctx._parentv) { _BaseCtx& ctx = *this;
// 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;
// parent is cancellable - glue to propagate cancel from it to us if (ctx._done != nil)
_BaseCtx *_parent = dynamic_cast<_BaseCtx *>(parent._ptr()); return ctx._done;
if (_parent != nil) { return ctx._parentv[0]->done();
_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;
} }
if (pforeignv.size() == 0) interface value(const void *key) {
return; _BaseCtx& ctx = *this;
// there are some foreign contexts to propagate cancel from for (auto parent : ctx._parentv) {
go([bctx,pforeignv]() { interface v = parent->value(key);
vector<_selcase> sel(1+pforeignv.size()); if (v != nil)
sel[0] = bctx->_done.recvs(); // 0 return v;
for (size_t i=0; i<pforeignv.size(); i++) }
sel[1+i] = pforeignv[i]->done().recvs(); // 1 + ... return nil;
}
int _ = select(sel); double deadline() {
_BaseCtx& ctx = *this;
// 0. nothing - already canceled double d = INFINITY;
if (_ > 0) for (auto parent : ctx._parentv) {
bctx->_cancel(pforeignv[_-1]->err()); 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