This is an automated email from the ASF dual-hosted git repository.
zwoop pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/master by this push:
new 464c61349a cripts: shrink Context from 3408 to 1920 bytes, expand
data[] to 16 (#13195)
464c61349a is described below
commit 464c61349a3ba9d251ad8f36c884e39c912dbd28
Author: Leif Hedstrom <[email protected]>
AuthorDate: Mon Jun 1 23:08:44 2026 -0600
cripts: shrink Context from 3408 to 1920 bytes, expand data[] to 16 (#13195)
* cripts: shrink Context from 3408 to 1920 bytes, expand data[] to 16
Pimpl Url::Path and Url::Query state behind unique_ptr so the heavy
vector/unordered_map/cripts::string members only allocate when a script
actually decomposes the path or query. Lazy-allocate Pristine, Parent,
and Remap From/To URLs in _UrlBlock — they're rarely all touched, and
the embedded 384-byte Url objects dominated the per-txn cost. Pimpl
Error::Reason for the same reason. Drop the unused INET6_ADDRSTRLEN
buffer in detail::ConnBase. Make cripts::Url's destructor virtual now
that we delete via unique_ptr. Bump CONTEXT_DATA_SLOTS from 4 to 16
so scripts have room to stash more per-txn state — the 384-byte cost
sits inside the budget freed by the URL/Connection cuts.
* Address Copilot's review comments
Bounds-check Path::Erase to avoid dereferencing a null _owner when
ix is out of range — operator[] returns a default-constructed String
in that case, and the subsequent p.operator=("") would then crash
inside String::operator=.
---
include/cripts/Connections.hpp | 7 +-
include/cripts/Context.hpp | 22 ++--
include/cripts/Error.hpp | 9 +-
include/cripts/Urls.hpp | 82 ++++++++++-----
src/cripts/Context.cc | 13 ++-
src/cripts/Error.cc | 10 +-
src/cripts/Urls.cc | 224 +++++++++++++++++++++++------------------
7 files changed, 215 insertions(+), 152 deletions(-)
diff --git a/include/cripts/Connections.hpp b/include/cripts/Connections.hpp
index a8e851d7ae..88a76e896e 100644
--- a/include/cripts/Connections.hpp
+++ b/include/cripts/Connections.hpp
@@ -459,10 +459,9 @@ protected:
void virtual _initialize() { _initialized = true; }
- cripts::Transaction *_state = nullptr;
- struct sockaddr const *_socket = nullptr;
- TSVConn _vc = nullptr;
- char _str[INET6_ADDRSTRLEN + 1];
+ cripts::Transaction *_state = nullptr;
+ struct sockaddr const *_socket = nullptr;
+ TSVConn _vc = nullptr;
bool _initialized = false;
}; // End class ConnBase
diff --git a/include/cripts/Context.hpp b/include/cripts/Context.hpp
index 00bc36eafc..19b92bc77a 100644
--- a/include/cripts/Context.hpp
+++ b/include/cripts/Context.hpp
@@ -18,6 +18,7 @@
#pragma once
#include <array>
+#include <memory>
#include <variant>
#include "ts/ts.h"
#include "ts/remap.h"
@@ -28,7 +29,7 @@
#include "cripts/Connections.hpp"
// These are pretty arbitrary for now
-constexpr int CONTEXT_DATA_SLOTS = 4;
+constexpr int CONTEXT_DATA_SLOTS = 16;
namespace cripts
{
@@ -132,23 +133,16 @@ public:
} _cache;
struct _UrlBlock {
- cripts::Client::URL &request;
- cripts::Pristine::URL pristine;
- cripts::Parent::URL parent;
+ cripts::Client::URL &request;
+ std::unique_ptr<cripts::Pristine::URL> pristine;
+ std::unique_ptr<cripts::Parent::URL> parent;
struct {
- cripts::Remap::From::URL from;
- cripts::Remap::To::URL to;
+ std::unique_ptr<cripts::Remap::From::URL> from;
+ std::unique_ptr<cripts::Remap::To::URL> to;
} remap;
- _UrlBlock(Context *ctx, cripts::Client::URL &alias) : request(alias)
- {
- request.set_context(ctx);
- pristine.set_context(ctx);
- parent.set_context(ctx);
- remap.from.set_context(ctx);
- remap.to.set_context(ctx);
- }
+ _UrlBlock(Context *ctx, cripts::Client::URL &alias) : request(alias) {
request.set_context(ctx); }
} _urls;
}; // End class Context
diff --git a/include/cripts/Error.hpp b/include/cripts/Error.hpp
index da674abe60..f0957c7bf6 100644
--- a/include/cripts/Error.hpp
+++ b/include/cripts/Error.hpp
@@ -17,6 +17,7 @@
*/
#pragma once
+#include <memory>
#include <utility>
#include "ts/ts.h"
@@ -132,10 +133,10 @@ public:
void Execute(cripts::Context *context);
private:
- Reason _reason;
- Status _status;
- bool _failed = false;
- bool _redirect = false;
+ std::unique_ptr<Reason> _reason;
+ Status _status;
+ bool _failed = false;
+ bool _redirect = false;
};
} // namespace cripts
diff --git a/include/cripts/Urls.hpp b/include/cripts/Urls.hpp
index c28f1d1dab..73580aa068 100644
--- a/include/cripts/Urls.hpp
+++ b/include/cripts/Urls.hpp
@@ -18,6 +18,7 @@
#pragma once
#include <algorithm>
+#include <memory>
#include <string>
#include <string_view>
#include <unordered_map>
@@ -338,7 +339,7 @@ public:
cripts::string_view GetSV() override;
cripts::string operator+=(cripts::string_view add);
- self_type operator=(cripts::string_view path);
+ self_type &operator=(cripts::string_view path);
String operator[](Segments::size_type ix);
void
@@ -346,8 +347,10 @@ public:
{
auto p = operator[](ix);
- _size -= p.size();
- p.operator=("");
+ if (_state && ix < _state->segments.size()) {
+ _state->size -= p.size();
+ p.operator=("");
+ }
}
void
@@ -368,7 +371,7 @@ public:
void
Flush()
{
- if (_modified) {
+ if (_state && _state->modified) {
operator=(GetSV());
}
}
@@ -376,10 +379,23 @@ public:
private:
void _parser();
- bool _modified = false;
- Segments _segments; // Lazy loading on this
- cripts::string _storage; // Used when recombining the segments
into a full path
- cripts::string::size_type _size = 0; // Mostly a guestimate for managing
_storage
+ struct State {
+ bool modified = false;
+ Segments segments; // Ordered list of path segments
+ cripts::string storage; // Used when recombining the
segments into a full path
+ cripts::string::size_type size = 0; // Mostly a guestimate for managing
storage
+ };
+
+ State &
+ _ensure_state()
+ {
+ if (!_state) {
+ _state = std::make_unique<State>();
+ }
+ return *_state;
+ }
+
+ std::unique_ptr<State> _state; // Lazily allocated when path is parsed or
modified
}; // End class Url::Path
@@ -461,18 +477,18 @@ public:
using Component::Component;
- Query(cripts::string_view load)
+ Query(cripts::string_view load) : _state(std::make_unique<State>())
{
- _data = load;
- _size = load.size();
- _loaded = true;
- _standalone = true;
+ _data = load;
+ _state->size = load.size();
+ _loaded = true;
+ _state->standalone = true;
}
void Reset() override;
cripts::string_view GetSV() override;
- self_type operator=(cripts::string_view query);
+ self_type &operator=(cripts::string_view query);
cripts::string operator+=(cripts::string_view add);
Parameter operator[](cripts::string_view param);
void Erase(cripts::string_view param);
@@ -482,7 +498,9 @@ public:
Erase()
{
operator=("");
- _size = 0;
+ if (_state) {
+ _state->size = 0;
+ }
}
void
@@ -503,14 +521,14 @@ public:
// Make sure the hash and vector are populated
_parser();
- std::ranges::sort(_ordered);
- _modified = true;
+ std::ranges::sort(_state->ordered);
+ _state->modified = true;
}
void
Flush()
{
- if (_modified) {
+ if (_state && _state->modified) {
operator=(GetSV());
}
}
@@ -518,19 +536,33 @@ public:
private:
void _parser();
- bool _modified = false;
- bool _standalone = false; // This component is used outside of
a URL owner, not common
- OrderedParams _ordered; // Ordered vector of all parameters,
can be sorted etc.
- HashParams _hashed; // Unordered map to go from "name" to
the query parameter
- cripts::string _storage; // Used when recombining the query
params into a
- // full query string
- cripts::string::size_type _size = 0; // Mostly a guesttimate
+ struct State {
+ bool modified = false;
+ bool standalone = false; // This component is used
outside of a URL owner, not common
+ OrderedParams ordered; // Ordered vector of all
parameters, can be sorted etc.
+ HashParams hashed; // Unordered map to go
from "name" to the query parameter
+ cripts::string storage; // Used when recombining
the query params into a full query string
+ cripts::string::size_type size = 0; // Mostly a guesttimate
+ };
+
+ State &
+ _ensure_state()
+ {
+ if (!_state) {
+ _state = std::make_unique<State>();
+ }
+ return *_state;
+ }
+
+ std::unique_ptr<State> _state; // Lazily allocated when query is parsed or
modified
}; // End class Url::Query
public:
Url() : scheme(this), host(this), port(this), path(this), query(this) {}
+ virtual ~Url() = default;
+
// Clear anything "cached" in the Url, this is rather draconian, but it's
safe...
virtual void
Reset()
diff --git a/src/cripts/Context.cc b/src/cripts/Context.cc
index 76bcc822b2..fe1a2ce389 100644
--- a/src/cripts/Context.cc
+++ b/src/cripts/Context.cc
@@ -48,13 +48,12 @@ Context::reset()
_server.request.Reset();
}
- // Clear the initialized URLs before calling next hook
- if (_urls.pristine.Initialized()) {
- _urls.pristine.Reset();
- }
- if (_urls.parent.Initialized()) {
- _urls.parent.Reset();
- }
+ // Release lazy URLs entirely — they get recreated on demand for the next
txn.
+ _urls.pristine.reset();
+ _urls.parent.reset();
+ _urls.remap.from.reset();
+ _urls.remap.to.reset();
+
if (_cache.url.Initialized()) {
_cache.url.Reset();
}
diff --git a/src/cripts/Error.cc b/src/cripts/Error.cc
index 63935a5206..f93ea90b92 100644
--- a/src/cripts/Error.cc
+++ b/src/cripts/Error.cc
@@ -41,7 +41,10 @@ void
Error::Reason::_set(cripts::Context *context, const cripts::string_view msg)
{
context->state.error.Fail();
- context->state.error._reason._setter(msg);
+ if (!context->state.error._reason) {
+ context->state.error._reason = std::make_unique<Reason>();
+ }
+ context->state.error._reason->_setter(msg);
}
// For convenience, an optional Reason message can also be specified with the
status
@@ -52,7 +55,10 @@ Error::Status::_set(cripts::Context *context, TSHttpStatus
status, const cripts:
context->state.error._status._setter(status);
if (msg.size() > 0) {
- context->state.error._reason._setter(msg);
+ if (!context->state.error._reason) {
+ context->state.error._reason = std::make_unique<Reason>();
+ }
+ context->state.error._reason->_setter(msg);
}
if (context->state.error.Redirected() || status ==
TS_HTTP_STATUS_MOVED_PERMANENTLY ||
diff --git a/src/cripts/Urls.cc b/src/cripts/Urls.cc
index f47e49f371..044b0c551a 100644
--- a/src/cripts/Urls.cc
+++ b/src/cripts/Urls.cc
@@ -112,25 +112,27 @@ Url::Port::operator=(int port)
cripts::string_view
Url::Path::GetSV()
{
- if (_segments.size() > 0) {
+ if (_state && _state->segments.size() > 0) {
std::ostringstream path;
- std::ranges::copy(_segments,
std::ostream_iterator<cripts::string_view>(path, "/"));
- _storage.reserve(_size);
- _storage = std::string_view(path.str());
- if (_storage.size() > 0) {
- _storage.pop_back(); // Removes the trailing /
+ std::ranges::copy(_state->segments,
std::ostream_iterator<cripts::string_view>(path, "/"));
+ _state->storage.reserve(_state->size);
+ _state->storage = std::string_view(path.str());
+ if (_state->storage.size() > 0) {
+ _state->storage.pop_back(); // Removes the trailing /
}
- return {_storage};
+ return {_state->storage};
} else if (_owner && _data.empty()) {
const char *value = nullptr;
int len = 0;
_ensure_initialized(_owner);
- value = TSUrlPathGet(_owner->_bufp, _owner->_urlp, &len);
- _data = cripts::string_view(value, len);
- _size = len;
+ value = TSUrlPathGet(_owner->_bufp, _owner->_urlp, &len);
+ _data = cripts::string_view(value, len);
+ if (_state) {
+ _state->size = len;
+ }
_loaded = true;
}
@@ -144,14 +146,14 @@ Url::Path::operator[](Segments::size_type ix)
_ensure_initialized(_owner);
_parser(); // Make sure the segments are loaded
- if (ix < _segments.size()) {
- ret._initialize(_segments[ix], this, ix);
+ if (_state && ix < _state->segments.size()) {
+ ret._initialize(_state->segments[ix], this, ix);
}
return ret; // RVO
}
-Url::Path
+Url::Path &
Url::Path::operator=(cripts::string_view path)
{
_ensure_initialized(_owner);
@@ -183,10 +185,11 @@ Url::Path::String::operator=(const cripts::string_view
str)
{
_ensure_initialized(_owner->_owner);
CAssert(!_owner->_owner->ReadOnly()); // This can not be a read-only URL
- _owner->_size -= _owner->_segments[_ix].size();
- _owner->_segments[_ix] = str;
- _owner->_size += str.size();
- _owner->_modified = true;
+ CAssert(_owner->_state); // Should have been allocated by
operator[]/_parser()
+ _owner->_state->size -= _owner->_state->segments[_ix].size();
+ _owner->_state->segments[_ix] = str;
+ _owner->_state->size += str.size();
+ _owner->_state->modified = true;
return *this;
}
@@ -196,51 +199,53 @@ Url::Path::Reset()
{
Component::Reset();
- _segments.clear();
- _storage.clear();
- _size = 0;
- _modified = false;
+ _state.reset();
}
void
Url::Path::Push(cripts::string_view val)
{
_parser();
- _modified = true;
- _segments.push_back(val);
+ auto &s = _ensure_state();
+ s.modified = true;
+ s.segments.push_back(val);
}
void
Url::Path::Insert(Segments::size_type ix, cripts::string_view val)
{
_parser();
- _modified = true;
- _segments.insert(_segments.begin() + ix, val);
+ auto &s = _ensure_state();
+ s.modified = true;
+ s.segments.insert(s.segments.begin() + ix, val);
}
void
Url::Path::_parser()
{
- if (_segments.size() == 0) {
- _segments = Split('/');
+ auto &s = _ensure_state();
+
+ if (s.segments.size() == 0) {
+ s.segments = Split('/');
}
}
Url::Query::Parameter &
Url::Query::Parameter::operator=(const cripts::string_view str)
{
- CAssert(!_owner->_standalone);
+ CAssert(!_owner->_state || !_owner->_state->standalone);
_ensure_initialized(_owner->_owner);
CAssert(!_owner->_owner->ReadOnly()); // This can not be a read-only URL
- auto iter = _owner->_hashed.find(_name);
+ auto &s = _owner->_ensure_state();
+ auto iter = s.hashed.find(_name);
- if (iter != _owner->_hashed.end()) {
+ if (iter != s.hashed.end()) {
iter->second = str; // Can be an empty string here!
} else {
- _owner->_ordered.push_back(_name);
- _owner->_hashed[_name] = str;
+ s.ordered.push_back(_name);
+ s.hashed[_name] = str;
}
- _owner->_modified = true;
+ s.modified = true;
return *this;
}
@@ -248,31 +253,31 @@ Url::Query::Parameter::operator=(const
cripts::string_view str)
cripts::string_view
Url::Query::GetSV()
{
- if (!_standalone) {
+ if (!_state || !_state->standalone) {
_ensure_initialized(_owner);
}
- if (_ordered.size() > 0) {
- _storage.clear();
- _storage.reserve(_size);
+ if (_state && _state->ordered.size() > 0) {
+ _state->storage.clear();
+ _state->storage.reserve(_state->size);
// ToDo: This is wonky, has to be a better std:: iteration to do here
- for (const auto key : _ordered) {
- auto iter = _hashed.find(key);
+ for (const auto key : _state->ordered) {
+ auto iter = _state->hashed.find(key);
- if (_storage.size() > 0) {
- _storage += "&";
+ if (_state->storage.size() > 0) {
+ _state->storage += "&";
}
- if (iter != _hashed.end()) {
- _storage += iter->first;
+ if (iter != _state->hashed.end()) {
+ _state->storage += iter->first;
if (iter->second.size() > 0) {
- _storage += '=';
- _storage += iter->second;
+ _state->storage += '=';
+ _state->storage += iter->second;
}
}
}
- return {_storage};
+ return {_state->storage};
}
// This gets weird when we modify the query parameter components, and can
possibly empty
@@ -282,19 +287,21 @@ Url::Query::GetSV()
const char *value = nullptr;
int len = 0;
- value = TSUrlHttpQueryGet(_owner->_bufp, _owner->_urlp, &len);
- _data = cripts::string_view(value, len);
- _size = len;
+ value = TSUrlHttpQueryGet(_owner->_bufp, _owner->_urlp, &len);
+ _data = cripts::string_view(value, len);
+ if (_state) {
+ _state->size = len;
+ }
_loaded = true;
}
return _data;
}
-Url::Query
+Url::Query &
Url::Query::operator=(cripts::string_view query)
{
- CAssert(!_standalone);
+ CAssert(!_state || !_state->standalone);
_ensure_initialized(_owner);
CAssert(!_owner->ReadOnly()); // This can not be a read-only URL
TSUrlHttpQuerySet(_owner->_bufp, _owner->_urlp, query.data(), query.size());
@@ -323,15 +330,15 @@ Url::Query::Parameter
Url::Query::operator[](cripts::string_view param)
{
// Make sure the hash and vector are populated, but only if we have an owner
- if (!_standalone) {
+ if (!_state || !_state->standalone) {
_ensure_initialized(_owner);
}
_parser();
Parameter ret;
- auto iter = _hashed.find(param);
+ auto iter = _state->hashed.find(param);
- if (iter != _hashed.end()) {
+ if (iter != _state->hashed.end()) {
ret._initialize(iter->first, iter->second, this);
} else {
ret._initialize(param, "", this);
@@ -346,21 +353,25 @@ Url::Query::Erase(cripts::string_view param)
// Make sure the hash and vector are populated
_parser();
- auto iter = _hashed.find(param);
- auto viter = std::ranges::find(_ordered, param);
+ auto &s = *_state;
+ auto iter = s.hashed.find(param);
+ auto viter = std::ranges::find(s.ordered, param);
- if (iter != _hashed.end()) {
- _size -= iter->second.size(); // Size of the erased value
- _hashed.erase(iter);
+ if (iter != s.hashed.end()) {
+ s.size -= iter->second.size(); // Size of the erased value
+ s.hashed.erase(iter);
- CAssert(viter != _ordered.end());
- _size -= viter->size(); // Length of the erased key
- _ordered.erase(viter);
+ CAssert(viter != s.ordered.end());
+ s.size -= viter->size(); // Length of the erased key
+ s.ordered.erase(viter);
- if (_ordered.size() == 0) {
+ if (s.ordered.size() == 0) {
Reset();
+ // After Reset() the state is gone; re-create it just so _modified can
be tracked.
+ _ensure_state().modified = true;
+ } else {
+ s.modified = true;
}
- _modified = true; // Make sure to set this after we reset above ...
}
}
@@ -371,21 +382,23 @@
Url::Query::Erase(std::initializer_list<cripts::string_view> list, bool keep)
// Make sure the hash and vector are populated
_parser();
- for (auto viter = _ordered.begin(); viter != _ordered.end();) {
+ auto &s = *_state;
+
+ for (auto viter = s.ordered.begin(); viter != s.ordered.end();) {
if (list.end() == std::ranges::find(list, *viter)) {
- auto iter = _hashed.find(*viter);
-
- CAssert(iter != _hashed.end());
- _size -= iter->second.size(); // Size of the erased value
- _size -= viter->size(); // Length of the erased key
- _hashed.erase(iter);
- viter = _ordered.erase(viter);
- _modified = true;
+ auto iter = s.hashed.find(*viter);
+
+ CAssert(iter != s.hashed.end());
+ s.size -= iter->second.size(); // Size of the erased value
+ s.size -= viter->size(); // Length of the erased key
+ s.hashed.erase(iter);
+ viter = s.ordered.erase(viter);
+ s.modified = true;
} else {
++viter;
}
}
- if (_ordered.size() == 0) {
+ if (s.ordered.size() == 0) {
Reset();
}
} else {
@@ -400,17 +413,15 @@ Url::Query::Reset()
{
Component::Reset();
- _ordered.clear();
- _hashed.clear();
- _storage.clear();
- _size = 0;
- _modified = false;
+ _state.reset();
}
void
Url::Query::_parser()
{
- if (_ordered.size() == 0) {
+ auto &s = _ensure_state();
+
+ if (s.ordered.size() == 0) {
for (const auto sv : Split('&')) {
const auto eq = sv.find_first_of('=');
cripts::string_view key = sv.substr(0, eq);
@@ -420,8 +431,8 @@ Url::Query::_parser()
val = sv.substr(eq + 1);
}
- _ordered.push_back(key); // Keep the order
- _hashed[key] = val;
+ s.ordered.push_back(key); // Keep the order
+ s.hashed[key] = val;
}
}
}
@@ -447,17 +458,21 @@ Url::String()
Pristine::URL &
Pristine::URL::_get(cripts::Context *context)
{
- _ensure_initialized(&context->_urls.pristine);
- return context->_urls.pristine;
+ auto &slot = context->_urls.pristine;
+
+ if (!slot) {
+ slot = std::make_unique<Pristine::URL>();
+ slot->set_context(context);
+ }
+ _ensure_initialized(slot.get());
+ return *slot;
}
void
Pristine::URL::_initialize()
{
- Pristine::URL *url = &_context->_urls.pristine;
-
TSAssert(_context->state.txnp);
- if (TSHttpTxnPristineUrlGet(_context->state.txnp, &url->_bufp, &url->_urlp)
!= TS_SUCCESS) {
+ if (TSHttpTxnPristineUrlGet(_context->state.txnp, &_bufp, &_urlp) !=
TS_SUCCESS) {
_context->state.error.Fail();
} else {
super_type::_initialize(); // Only if successful
@@ -519,8 +534,14 @@ Remap::From::URL::_initialize()
Remap::From::URL &
Remap::From::URL::_get(cripts::Context *context)
{
- _ensure_initialized(&context->_urls.remap.from);
- return context->_urls.remap.from;
+ auto &slot = context->_urls.remap.from;
+
+ if (!slot) {
+ slot = std::make_unique<Remap::From::URL>();
+ slot->set_context(context);
+ }
+ _ensure_initialized(slot.get());
+ return *slot;
}
void
@@ -539,8 +560,14 @@ Remap::To::URL::_initialize()
Remap::To::URL &
Remap::To::URL::_get(cripts::Context *context)
{
- _ensure_initialized(&context->_urls.remap.to);
- return context->_urls.remap.to;
+ auto &slot = context->_urls.remap.to;
+
+ if (!slot) {
+ slot = std::make_unique<Remap::To::URL>();
+ slot->set_context(context);
+ }
+ _ensure_initialized(slot.get());
+ return *slot;
}
Cache::URL &
@@ -623,19 +650,24 @@ Cache::URL::_update()
Parent::URL &
Parent::URL::_get(cripts::Context *context)
{
- _ensure_initialized(&context->_urls.parent);
- return context->_urls.parent;
+ auto &slot = context->_urls.parent;
+
+ if (!slot) {
+ slot = std::make_unique<Parent::URL>();
+ slot->set_context(context);
+ }
+ _ensure_initialized(slot.get());
+ return *slot;
}
void
Parent::URL::_initialize()
{
- Parent::URL *url = &_context->_urls.parent;
Client::Request &req = Client::Request::_get(_context); // Repurpose /
create the shared request object
- if (TSUrlCreate(req.BufP(), &url->_urlp) == TS_SUCCESS) {
+ if (TSUrlCreate(req.BufP(), &_urlp) == TS_SUCCESS) {
TSAssert(_context->state.txnp);
- if (TSHttpTxnParentSelectionUrlGet(_context->state.txnp, req.BufP(),
url->_urlp) != TS_SUCCESS) {
+ if (TSHttpTxnParentSelectionUrlGet(_context->state.txnp, req.BufP(),
_urlp) != TS_SUCCESS) {
_context->state.error.Fail();
return;
}