diff --git a/include/chainblocks.h b/include/chainblocks.h index fe6ce2fb3b2..f0e3dcb4b22 100644 --- a/include/chainblocks.h +++ b/include/chainblocks.h @@ -45,12 +45,10 @@ enum CBType : uint8_t { enum CBChainState : uint8_t { Continue, // Nothing happened, continue - Rebase, // Continue this chain but put the local chain initial input as next - // input - Restart, // Restart the local chain from the top (notice not the root!) - Return, // Used in conditional paths, end this chain and return previous - // output - Stop, // Stop the chain execution (including root) + Return, // Control flow, end this chain/flow and return previous output + Rebase, // Continue but put the local chain initial input as next input + Restart, // Restart the current chain from the top (non inline chains) + Stop // Stop the flow execution }; // These blocks run fully inline in the runchain threaded execution engine @@ -476,11 +474,7 @@ struct CBFlow { struct CBVarPayload { union { -#if defined(__cplusplus) || defined(CB_USE_ENUMS) - enum CBChainState chainState; -#else - CBChainState chainState; -#endif + CBBool boolValue; struct { CBPointer objectValue; @@ -488,8 +482,6 @@ struct CBVarPayload { int32_t objectTypeId; }; - CBBool boolValue; - CBInt intValue; CBInt2 int2Value; CBInt3 int3Value; @@ -776,7 +768,7 @@ typedef void(__cdecl *CBThrowException)(const char *errorText) typedef void(__cdecl *CBThrowExceptionSimple)() __attribute__((noreturn)); -typedef struct CBVar(__cdecl *CBSuspend)(struct CBContext *context, +typedef CBChainState(__cdecl *CBSuspend)(struct CBContext *context, double seconds); typedef void(__cdecl *CBCloneVar)(struct CBVar *dst, const struct CBVar *src); @@ -800,9 +792,19 @@ typedef struct CBValidationResult(__cdecl *CBValidateBlocks)( CBlocks blocks, CBValidationCallback callback, void *userData, struct CBInstanceData data); -typedef struct CBVar(__cdecl *CBRunBlocks)(CBlocks blocks, +#if defined(__cplusplus) || defined(CB_USE_ENUMS) +typedef enum CBChainState(__cdecl *CBRunBlocks)(CBlocks blocks, + struct CBContext *context, + struct CBVar input, + struct CBVar *output, + const CBBool handleReturn); +#else +typedef CBChainState(__cdecl *CBRunBlocks)(CBlocks blocks, struct CBContext *context, - struct CBVar input); + struct CBVar input, + struct CBVar *output, + const CBBool handleReturn); +#endif typedef void(__cdecl *CBLog)(const char *msg); @@ -892,8 +894,6 @@ struct CBCore { // before calling any of those make sure to release // and call destructors manually! CBThrowException throwException; - CBThrowExceptionSimple throwCancellation; - CBThrowExceptionSimple throwRestart; // To be used within blocks, to suspend the coroutine CBSuspend suspend; diff --git a/include/chainblocks.hpp b/include/chainblocks.hpp index 07d07950331..dc23e15fd4f 100644 --- a/include/chainblocks.hpp +++ b/include/chainblocks.hpp @@ -40,18 +40,6 @@ class ActivationError : public CBException { bool fatal; }; -class ChainCancellation : public ActivationError { -public: - ChainCancellation() : ActivationError("", CBChainState::Stop, false) {} -}; - -using ChainStop = ChainCancellation; - -class ChainRestart : public ActivationError { -public: - ChainRestart() : ActivationError("", CBChainState::Restart, false) {} -}; - class ComposeError : public CBException { public: explicit ComposeError(std::string_view msg, bool fatal = true) @@ -205,10 +193,7 @@ struct Parameters { }; struct Var : public CBVar { - explicit Var() : CBVar() { - valueType = None; - payload.chainState = CBChainState::Continue; - } + explicit Var() : CBVar() { valueType = None; } explicit Var(const CBVar &other) { memcpy((void *)this, (void *)&other, sizeof(CBVar)); @@ -276,58 +261,9 @@ struct Var : public CBVar { } } - constexpr static CBVar Empty() { - CBVar res{}; - return res; - } - - constexpr static CBVar True() { - CBVar res{}; - res.valueType = CBType::Bool; - // notice we need to use chain state - // as this is the active member - // should be fixed in c++20 tho - res.payload.chainState = (CBChainState)1; - return res; - } - - constexpr static CBVar False() { - CBVar res{}; - res.valueType = CBType::Bool; - // notice we need to use chain state - // as this is the active member - // should be fixed in c++20 tho - res.payload.chainState = (CBChainState)0; - return res; - } - - constexpr static CBVar Stop() { - CBVar res{}; - res.valueType = None; - res.payload.chainState = CBChainState::Stop; - return res; - } - - constexpr static CBVar Restart() { - CBVar res{}; - res.valueType = None; - res.payload.chainState = CBChainState::Restart; - return res; - } - - constexpr static CBVar Return() { - CBVar res{}; - res.valueType = None; - res.payload.chainState = CBChainState::Return; - return res; - } - - constexpr static CBVar Rebase() { - CBVar res{}; - res.valueType = None; - res.payload.chainState = CBChainState::Rebase; - return res; - } + constexpr static CBVar Empty{}; + constexpr static CBVar True{{true}, {nullptr}, 0, CBType::Bool}; + constexpr static CBVar False{{false}, {nullptr}, 0, CBType::Bool}; template static Var Object(T valuePtr, uint32_t objectVendorId, diff --git a/include/dllblock.hpp b/include/dllblock.hpp index 6ba6a4c663c..bcda27e8437 100644 --- a/include/dllblock.hpp +++ b/include/dllblock.hpp @@ -109,15 +109,7 @@ class Core { sCore._core.throwException(errorText); } - [[noreturn]] static void throwCancellation(const char *errorText) { - sCore._core.throwCancellation(); - } - - [[noreturn]] static void throwRestart(const char *errorText) { - sCore._core.throwRestart(); - } - - static CBVar suspend(CBContext *context, double seconds) { + static CBChainState suspend(CBContext *context, double seconds) { return sCore._core.suspend(context, seconds); } @@ -180,8 +172,10 @@ class Core { return sCore._core.validateBlocks(blocks, callback, userData, data); } - static CBVar runBlocks(CBlocks blocks, CBContext *context, CBVar input) { - return sCore._core.runBlocks(blocks, context, input); + static CBChainState runBlocks(CBlocks blocks, CBContext *context, CBVar input, + CBVar *output, + const bool handleReturn = false) { + return sCore._core.runBlocks(blocks, context, input, output, handleReturn); } static void log(const char *msg) { sCore._core.log(msg); } diff --git a/include/ops.hpp b/include/ops.hpp index ef0efd3eb79..b1385ec6d65 100644 --- a/include/ops.hpp +++ b/include/ops.hpp @@ -186,13 +186,12 @@ ALWAYS_INLINE inline bool operator==(const CBVar &a, const CBVar &b) { return false; switch (a.valueType) { + case None: case CBType::Any: case EndOfBlittableTypes: return true; case StackIndex: return a.payload.stackIndexValue == b.payload.stackIndexValue; - case None: - return a.payload.chainState == b.payload.chainState; case Object: return a.payload.objectValue == b.payload.objectValue; case Enum: @@ -403,8 +402,6 @@ ALWAYS_INLINE inline bool operator<(const CBVar &a, const CBVar &b) { return false; switch (a.valueType) { - case None: - return a.payload.chainState < b.payload.chainState; case StackIndex: return a.payload.stackIndexValue < b.payload.stackIndexValue; case Enum: @@ -506,6 +503,7 @@ ALWAYS_INLINE inline bool operator<(const CBVar &a, const CBVar &b) { case Block: case Object: case CBType::Any: + case None: case EndOfBlittableTypes: throw chainblocks::InvalidVarTypeError( "Comparison operator < not supported for the given type: " + @@ -597,8 +595,6 @@ ALWAYS_INLINE inline bool operator<=(const CBVar &a, const CBVar &b) { return false; switch (a.valueType) { - case None: - return a.payload.chainState <= b.payload.chainState; case StackIndex: return a.payload.stackIndexValue <= b.payload.stackIndexValue; case Enum: @@ -700,6 +696,7 @@ ALWAYS_INLINE inline bool operator<=(const CBVar &a, const CBVar &b) { case Block: case Object: case CBType::Any: + case None: case EndOfBlittableTypes: throw chainblocks::InvalidVarTypeError( "Comparison operator <= not supported for the given type: " + @@ -873,8 +870,6 @@ template <> struct hash { auto res = hash()(int(var.valueType)); switch (var.valueType) { case None: - MAGIC_HASH(var.payload.chainState); - break; case Any: break; case Object: diff --git a/include/utility.hpp b/include/utility.hpp index 3c37fc31409..8f5c5b69b5d 100644 --- a/include/utility.hpp +++ b/include/utility.hpp @@ -246,8 +246,9 @@ template class TBlocksVar { return _chainValidation; } - CBVar activate(CBContext *context, const CBVar &input) { - return CB_CORE::runBlocks(_blocks, context, input); + CBChainState activate(CBContext *context, const CBVar &input, CBVar &output, + const bool handleReturn = false) { + return CB_CORE::runBlocks(_blocks, context, input, &output, handleReturn); } operator bool() const { return _blocksArray.size() > 0; } @@ -285,14 +286,9 @@ template struct AsyncOp { // Wait suspending! while (true) { auto state = asyncRes.wait_for(std::chrono::seconds(0)); - if (state == std::future_status::ready) + if (state == std::future_status::ready || + CB_CORE::suspend(_context, 0) != CBChainState::Continue) break; - auto chainState = CB_CORE::suspend(_context, 0); - if (chainState.payload.chainState == Restart) { - CB_CORE::throwRestart(); - } else if (chainState.payload.chainState != Continue) { - CB_CORE::throwCancellation(); - } } // This should also throw if we had exceptions return asyncRes.get(); @@ -301,14 +297,9 @@ template struct AsyncOp { CBVar operator()(std::future &fut) { while (true) { auto state = fut.wait_for(std::chrono::seconds(0)); - if (state == std::future_status::ready) + if (state == std::future_status::ready || + CB_CORE::suspend(_context, 0) != CBChainState::Continue) break; - auto chainState = CB_CORE::suspend(_context, 0); - if (chainState.payload.chainState == Restart) { - CB_CORE::throwRestart(); - } else if (chainState.payload.chainState != Continue) { - CB_CORE::throwCancellation(); - } } // This should also throw if we had exceptions return fut.get(); @@ -317,14 +308,9 @@ template struct AsyncOp { void operator()(std::future &fut) { while (true) { auto state = fut.wait_for(std::chrono::seconds(0)); - if (state == std::future_status::ready) + if (state == std::future_status::ready || + CB_CORE::suspend(_context, 0) != CBChainState::Continue) break; - auto chainState = CB_CORE::suspend(_context, 0); - if (chainState.payload.chainState == Restart) { - CB_CORE::throwRestart(); - } else if (chainState.payload.chainState != Continue) { - CB_CORE::throwCancellation(); - } } // This should also throw if we had exceptions fut.get(); @@ -354,14 +340,9 @@ template struct AsyncOp { while (true) { auto state = fut.wait_for(std::chrono::seconds(0)); - if (state == std::future_status::ready) + if (state == std::future_status::ready || + CB_CORE::suspend(_context, 0) != CBChainState::Continue) break; - auto chainState = CB_CORE::suspend(_context, 0); - if (chainState.payload.chainState == Restart) { - CB_CORE::throwRestart(); - } else if (chainState.payload.chainState != Continue) { - CB_CORE::throwCancellation(); - } } if (p) @@ -395,14 +376,9 @@ template struct AsyncOp { while (true) { auto state = fut.wait_for(std::chrono::seconds(0)); - if (state == std::future_status::ready) + if (state == std::future_status::ready || + CB_CORE::suspend(_context, 0) != CBChainState::Continue) break; - auto chainState = CB_CORE::suspend(_context, 0); - if (chainState.payload.chainState == Restart) { - CB_CORE::throwRestart(); - } else if (chainState.payload.chainState != Continue) { - CB_CORE::throwCancellation(); - } } if (p) diff --git a/src/core/blocks/chains.cpp b/src/core/blocks/chains.cpp index a2f0ee29602..059ee0d9d12 100644 --- a/src/core/blocks/chains.cpp +++ b/src/core/blocks/chains.cpp @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: BSD 3-Clause "New" or "Revised" License */ /* Copyright © 2019-2020 Giovanni Petrantoni */ +#include "foundation.hpp" #include "shared.hpp" #include #include @@ -22,16 +23,17 @@ struct ChainBase { static inline Types ChainTypes{ {CoreInfo::ChainType, CoreInfo::StringType, CoreInfo::NoneType}}; + static inline Types ChainVarTypes{ChainTypes, {CoreInfo::ChainVarType}}; + static inline ParamsInfo waitChainParamsInfo = ParamsInfo( - ParamsInfo::Param("Chain", "The chain to run.", ChainTypes), + ParamsInfo::Param("Chain", "The chain to wait.", ChainVarTypes), ParamsInfo::Param("Once", "Runs this sub-chain only once within the parent chain " "execution cycle.", CoreInfo::BoolType), - ParamsInfo::Param( - "Passthrough", - "The input of this block will be the output. Always on if Detached.", - CoreInfo::BoolType)); + ParamsInfo::Param("Passthrough", + "The input of this block will be the output.", + CoreInfo::BoolType)); static inline ParamsInfo runChainParamsInfo = ParamsInfo( ParamsInfo::Param("Chain", "The chain to run.", ChainTypes), @@ -80,12 +82,12 @@ struct ChainBase { static inline ParamsInfo chainOnlyParamsInfo = ParamsInfo(ParamsInfo::Param("Chain", "The chain to run.", ChainTypes)); - CBVar chainref; + ParamVar chainref{}; std::shared_ptr chain; - bool once; - bool doneOnce; - bool passthrough; - RunChainMode mode; + bool once = false; + bool doneOnce = false; + bool passthrough = false; + RunChainMode mode = RunChainMode::Inline; CBValidationResult chainValidation{}; void destroy() { chainblocks::arrayFree(chainValidation.exposedInfo); } @@ -112,10 +114,11 @@ struct ChainBase { chainblocks::arrayFree(chainValidation.exposedInfo); // Actualize the chain here... - if (chainref.valueType == CBType::Chain) { - chain = CBChain::sharedFromRef(chainref.payload.chainValue); - } else if (chainref.valueType == String) { - chain = chainblocks::Globals::GlobalChains[chainref.payload.stringValue]; + if (chainref->valueType == CBType::Chain) { + chain = CBChain::sharedFromRef(chainref->payload.chainValue); + } else if (chainref->valueType == String) { + // TODO this should be mutex protected + chain = chainblocks::Globals::GlobalChains[chainref->payload.stringValue]; } else { chain = nullptr; } @@ -152,14 +155,18 @@ struct ChainBase { ? data.inputType : chainValidation.outputType; } + + void cleanup() { chainref.cleanup(); } + + void warmup(CBContext *ctx) { chainref.warmup(ctx); } }; struct WaitChain : public ChainBase { - CBVar _output{}; + OwnedVar _output{}; void cleanup() { + ChainBase::cleanup(); doneOnce = false; - destroyVar(_output); } static CBParametersInfo parameters() { @@ -169,7 +176,7 @@ struct WaitChain : public ChainBase { void setParam(int index, CBVar value) { switch (index) { case 0: - cloneVar(chainref, value); + chainref = value; break; case 1: once = value.payload.boolValue; @@ -197,7 +204,20 @@ struct WaitChain : public ChainBase { } CBVar activate(CBContext *context, const CBVar &input) { + if (unlikely(!chain && chainref.isVariable())) { + auto vchain = chainref.get(); + if (vchain.valueType == CBType::Chain) { + chain = CBChain::sharedFromRef(vchain.payload.chainValue); + } else if (vchain.valueType == String) { + // TODO this should be mutex protected + chain = chainblocks::Globals::GlobalChains[vchain.payload.stringValue]; + } else { + chain = nullptr; + } + } + if (unlikely(!chain)) { + LOG(WARNING) << "WaitChain's chain is void."; return input; } else if (!doneOnce) { if (once) @@ -210,7 +230,8 @@ struct WaitChain : public ChainBase { if (passthrough) { return input; } else { - cloneVar(_output, chain->finishedOutput); + // ownedvar, will clone + _output = chain->finishedOutput; return _output; } } else { @@ -233,7 +254,7 @@ struct Resume : public ChainBase { return data.inputType; } - void setParam(int index, CBVar value) { cloneVar(chainref, value); } + void setParam(int index, CBVar value) { chainref = value; } CBVar getParam(int index) { return chainref; } @@ -318,6 +339,7 @@ struct BaseRunner : public ChainBase { tryStopChain(); _steppedFlow.chain = nullptr; doneOnce = false; + ChainBase::cleanup(); } void doWarmup(CBContext *context) { @@ -378,7 +400,7 @@ struct RunChain : public BaseRunner { void setParam(int index, CBVar value) { switch (index) { case 0: - cloneVar(chainref, value); + chainref = value; break; case 1: once = value.payload.boolValue; @@ -412,7 +434,10 @@ struct RunChain : public BaseRunner { return Var(); } - void warmup(CBContext *context) { doWarmup(context); } + void warmup(CBContext *context) { + ChainBase::warmup(context); + doWarmup(context); + } CBVar activate(CBContext *context, const CBVar &input) { if (unlikely(!chain)) @@ -433,8 +458,11 @@ struct RunChain : public BaseRunner { chain->flow = context->main->flow; chain->node = context->main->node; auto runRes = runSubChain(chain.get(), context, input); - if (unlikely(runRes.state == Failed || context->aborted)) { - return StopChain; + if (unlikely(runRes.state == Failed)) { + // meaning there was an exception while + // running the sub chain, stop the parent too + context->stopFlow(runRes.output); + return runRes.output; } else if (passthrough) { return input; } else { @@ -505,11 +533,10 @@ template struct BaseLoader : public BaseRunner { chain->flow = context->main->flow; chain->node = context->main->node; auto runRes = runSubChain(chain.get(), context, input); - if (unlikely(runRes.state == Failed || context->aborted)) { - return StopChain; - } else { - return input; + if (unlikely(runRes.state == Failed)) { + context->stopFlow(input); } + return input; } } else { return input; @@ -648,7 +675,10 @@ struct ChainRunner : public BaseLoader { _chain.cleanup(); } - void warmup(CBContext *context) { _chain.warmup(context); } + void warmup(CBContext *context) { + BaseLoader::warmup(context); + _chain.warmup(context); + } void composeChain() { CBInstanceData data{}; diff --git a/src/core/blocks/channels.cpp b/src/core/blocks/channels.cpp index fcd95aa1411..5ee6bfb7ea1 100644 --- a/src/core/blocks/channels.cpp +++ b/src/core/blocks/channels.cpp @@ -356,7 +356,8 @@ struct Consume : public Consumers { if (!_storage.empty()) { return _storage; } else { - return StopChain; + context->stopFlow(Var::Empty); + return Var::Empty; } } suspend(context, 0); @@ -429,7 +430,8 @@ struct Listen : public Consumers { if (!_storage.empty()) { return _storage; } else { - return StopChain; + context->stopFlow(Var::Empty); + return Var::Empty; } } suspend(context, 0); diff --git a/src/core/blocks/core.cpp b/src/core/blocks/core.cpp index 4d16cf58d96..59dfe3e209a 100644 --- a/src/core/blocks/core.cpp +++ b/src/core/blocks/core.cpp @@ -225,7 +225,7 @@ struct Sort : public ActionJointOp { CBVar _o; CBVar &operator()(CBVar &a) { - _o = _bu->_blks.activate(_ctx, a); + _bu->_blks.activate(_ctx, a, _o); return _o; } } blocksKeyFn; @@ -364,10 +364,15 @@ struct Remove : public ActionJointOp { ALWAYS_INLINE CBVar activate(CBContext *context, const CBVar &input) { JointOp::ensureJoinSetup(context); // Remove in place, will possibly remove any sorting! + CBVar output{}; const uint32_t len = _input->payload.seqValue.len; for (uint32_t i = len; i > 0; i--) { const auto &var = _input->payload.seqValue.elements[i - 1]; - if (_blks.activate(context, var) == True) { + // conditional flow so we might have "returns" form (And) (Or) + if (_blks.activate(context, var, output, true) > CBChainState::Return) + return *_input; + + if (output == Var::True) { // this is acceptable cos del ops don't call free or grow if (_fast) arrayDelFast(_input->payload.seqValue, i - 1); @@ -608,8 +613,12 @@ struct ForEachBlock { CBVar activate(CBContext *context, const CBVar &input) { IterableSeq in(input); + CBVar output{}; for (auto &item : in) { - _blocks.activate(context, item); + // handle return short circuit, assume it was for us + auto state = _blocks.activate(context, item, output, true); + if (state != CBChainState::Continue) + break; } return input; } @@ -659,10 +668,14 @@ struct Map { CBVar activate(CBContext *context, const CBVar &input) { IterableSeq in(input); + CBVar output{}; arrayResize(_output.payload.seqValue, 0); for (auto &item : in) { - auto res = _blocks.activate(context, item); - arrayPush(_output.payload.seqValue, res); + // handle return short circuit, assume it was for us + auto state = _blocks.activate(context, item, output, true); + if (state != CBChainState::Continue) + break; + arrayPush(_output.payload.seqValue, output); } return _output; } @@ -734,9 +747,14 @@ struct Reduce { throw ActivationError("Reduce: Input sequence was empty!"); } cloneVar(*_tmp, input.payload.seqValue.elements[0]); + CBVar output{}; for (uint32_t i = 1; i < input.payload.seqValue.len; i++) { - auto res = _blocks.activate(context, input.payload.seqValue.elements[i]); - cloneVar(*_tmp, res); + auto &item = input.payload.seqValue.elements[i]; + // allow short circut with (Return) + auto state = _blocks.activate(context, item, output, true); + if (state != CBChainState::Continue) + break; + cloneVar(*_tmp, output); } cloneVar(_output, *_tmp); return _output; @@ -1024,6 +1042,20 @@ RUNTIME_BLOCK_getParam(Push); RUNTIME_BLOCK_activate(Push); RUNTIME_BLOCK_END(Push); +// Register Sequence +RUNTIME_CORE_BLOCK_FACTORY(Sequence); +RUNTIME_BLOCK_cleanup(Sequence); +RUNTIME_BLOCK_warmup(Sequence); +RUNTIME_BLOCK_inputTypes(Sequence); +RUNTIME_BLOCK_outputTypes(Sequence); +RUNTIME_BLOCK_parameters(Sequence); +RUNTIME_BLOCK_compose(Sequence); +RUNTIME_BLOCK_exposedVariables(Sequence); +RUNTIME_BLOCK_setParam(Sequence); +RUNTIME_BLOCK_getParam(Sequence); +RUNTIME_BLOCK_activate(Sequence); +RUNTIME_BLOCK_END(Sequence); + // Register Pop RUNTIME_CORE_BLOCK_FACTORY(Pop); RUNTIME_BLOCK_cleanup(Pop); @@ -1105,6 +1137,7 @@ RUNTIME_BLOCK_END(DropFront); // Register Get RUNTIME_CORE_BLOCK_FACTORY(Get); RUNTIME_BLOCK_cleanup(Get); +RUNTIME_BLOCK_warmup(Get); RUNTIME_BLOCK_destroy(Get); RUNTIME_BLOCK_inputTypes(Get); RUNTIME_BLOCK_outputTypes(Get); @@ -1194,7 +1227,6 @@ RUNTIME_BLOCK_getParam(Repeat); RUNTIME_BLOCK_activate(Repeat); RUNTIME_BLOCK_cleanup(Repeat); RUNTIME_BLOCK_warmup(Repeat); -RUNTIME_BLOCK_exposedVariables(Repeat); RUNTIME_BLOCK_requiredVariables(Repeat); RUNTIME_BLOCK_compose(Repeat); RUNTIME_BLOCK_END(Repeat); @@ -1333,6 +1365,7 @@ void registerBlocksCoreBlocks() { REGISTER_CORE_BLOCK(Ref); REGISTER_CORE_BLOCK(Update); REGISTER_CORE_BLOCK(Push); + REGISTER_CORE_BLOCK(Sequence); REGISTER_CORE_BLOCK(Clear); REGISTER_CORE_BLOCK(Pop); REGISTER_CORE_BLOCK(PopFront); diff --git a/src/core/blocks/core.hpp b/src/core/blocks/core.hpp index e1f0a03d7d0..a781e9cb290 100644 --- a/src/core/blocks/core.hpp +++ b/src/core/blocks/core.hpp @@ -10,7 +10,36 @@ #include namespace chainblocks { + +enum class BasicTypes { + Any, + Bool, + Int, + Int2, + Int3, + Int4, + Int8, + Int16, + Float, + Float2, + Float3, + Float4, + Color, + Chain, + Block, + Bytes, + String, + Image +}; + struct CoreInfo { + static inline EnumInfo BasicTypesEnum{"Type", 'sink', 'type'}; + static inline Type BasicTypesType{ + {CBType::Enum, {.enumeration = {'sink', 'type'}}}}; + static inline Type BasicTypesSeqType{ + {CBType::Seq, {.seqTypes = BasicTypesType}}}; + static inline Types BasicTypesTypes{{BasicTypesType, BasicTypesSeqType}}; + static inline Type NoneType{{CBType::None}}; #define CB_CORE_TYPE_DEF(_cbtype_) \ @@ -97,7 +126,7 @@ struct CoreInfo { static inline Types BlocksOrNone{Blocks, {NoneType}}; - static inline Type BlocksOrNoneSeq{{CBType::Seq, .seqTypes = BlocksOrNone}}; + static inline Type BlocksOrNoneSeq{{CBType::Seq, {.seqTypes = BlocksOrNone}}}; static inline Types StringOrBytes{{StringType, BytesType}}; @@ -200,7 +229,7 @@ struct BaseOpsBin { case 0: return _value; default: - return Empty; + return Var::Empty; } } }; @@ -215,9 +244,9 @@ struct BaseOpsBin { } \ const auto &value = *_target; \ if (input OP value) { \ - return True; \ + return Var::True; \ } \ - return False; \ + return Var::False; \ } \ }; \ RUNTIME_CORE_BLOCK_TYPE(NAME); @@ -246,31 +275,31 @@ LOGIC_OP(IsLessEqual, <=); for (uint32_t i = 0; i < input.payload.seqValue.len; i++) { \ if (input.payload.seqValue.elements[i] OP value.payload.seqValue \ .elements[i]) { \ - return True; \ + return Var::True; \ } \ } \ - return False; \ + return Var::False; \ } else if (input.valueType == Seq && value.valueType != Seq) { \ for (uint32_t i = 0; i < input.payload.seqValue.len; i++) { \ if (input.payload.seqValue.elements[i] OP value) { \ - return True; \ + return Var::True; \ } \ } \ - return False; \ + return Var::False; \ } else if (input.valueType != Seq && value.valueType == Seq) { \ for (uint32_t i = 0; i < value.payload.seqValue.len; i++) { \ if (input OP value.payload.seqValue.elements[i]) { \ - return True; \ + return Var::True; \ } \ } \ - return False; \ + return Var::False; \ } else if (input.valueType != Seq && value.valueType != Seq) { \ if (input OP value) { \ - return True; \ + return Var::True; \ } \ - return False; \ + return Var::False; \ } \ - return False; \ + return Var::False; \ } \ }; \ RUNTIME_CORE_BLOCK_TYPE(NAME); @@ -292,31 +321,31 @@ LOGIC_OP(IsLessEqual, <=); for (uint32_t i = 0; i < input.payload.seqValue.len; i++) { \ if (!(input.payload.seqValue.elements[i] OP value.payload.seqValue \ .elements[i])) { \ - return False; \ + return Var::False; \ } \ } \ - return True; \ + return Var::True; \ } else if (input.valueType == Seq && value.valueType != Seq) { \ for (uint32_t i = 0; i < input.payload.seqValue.len; i++) { \ if (!(input.payload.seqValue.elements[i] OP value)) { \ - return False; \ + return Var::False; \ } \ } \ - return True; \ + return Var::True; \ } else if (input.valueType != Seq && value.valueType == Seq) { \ for (uint32_t i = 0; i < value.payload.seqValue.len; i++) { \ if (!(input OP value.payload.seqValue.elements[i])) { \ - return False; \ + return Var::False; \ } \ } \ - return True; \ + return Var::True; \ } else if (input.valueType != Seq && value.valueType != Seq) { \ if (!(input OP value)) { \ - return False; \ + return Var::False; \ } \ - return True; \ + return Var::True; \ } \ - return False; \ + return Var::False; \ } \ }; \ RUNTIME_CORE_BLOCK_TYPE(NAME); @@ -351,7 +380,8 @@ struct Input { static CBTypesInfo outputTypes() { return CoreInfo::AnyType; } ALWAYS_INLINE CBVar activate(CBContext *context, const CBVar &input) { - return RebaseChain; + context->rebaseFlow(); + return input; } }; @@ -443,10 +473,12 @@ struct And { ALWAYS_INLINE CBVar activate(CBContext *context, const CBVar &input) { if (input.payload.boolValue) { // Continue the flow - return RebaseChain; + context->rebaseFlow(); + return input; } else { - // Reason: We are done, input IS FALSE so we FAIL - return ReturnPrevious; + // Reason: We are done, input IS FALSE so we stop this flow + context->returnFlow(input); + return input; } } }; @@ -457,10 +489,12 @@ struct Or { ALWAYS_INLINE CBVar activate(CBContext *context, const CBVar &input) { if (input.payload.boolValue) { // Reason: We are done, input IS TRUE so we succeed - return ReturnPrevious; + context->returnFlow(input); + return input; } else { // Continue the flow, with the initial input as next input! - return RebaseChain; + context->rebaseFlow(); + return input; } } }; @@ -477,7 +511,8 @@ struct Stop { static CBTypesInfo inputTypes() { return CoreInfo::AnyType; } static CBTypesInfo outputTypes() { return CoreInfo::NoneType; } ALWAYS_INLINE CBVar activate(CBContext *context, const CBVar &input) { - return Var::Stop(); + context->stopFlow(input); + return input; } }; @@ -485,7 +520,8 @@ struct Restart { static CBTypesInfo inputTypes() { return CoreInfo::AnyType; } static CBTypesInfo outputTypes() { return CoreInfo::NoneType; } ALWAYS_INLINE CBVar activate(CBContext *context, const CBVar &input) { - return Var::Restart(); + context->restartFlow(input); + return input; } }; @@ -493,7 +529,8 @@ struct Return { static CBTypesInfo inputTypes() { return CoreInfo::AnyType; } static CBTypesInfo outputTypes() { return CoreInfo::NoneType; } ALWAYS_INLINE CBVar activate(CBContext *context, const CBVar &input) { - return Var::Return(); + context->returnFlow(input); + return input; } }; @@ -965,25 +1002,25 @@ struct Get : public VariableBase { return true; } + void warmup(CBContext *context) { + if (_global) + _target = referenceGlobalVariable(context, _name.c_str()); + else + _target = referenceVariable(context, _name.c_str()); + } + ALWAYS_INLINE CBVar activate(CBContext *context, const CBVar &input) { if (likely(_cell != nullptr)) return *_cell; - if (!_target) { - // notice we don't care about _global - // cos we reference straight into activate - // altho not optimal, but we mitigate with cell trick - // this allows any runtime settle of Set - _target = referenceVariable(context, _name.c_str()); - // sanity check that the variable is not pristine... - // the setter chain might have stopped actually! - if (_defaultValue.valueType == None && _target->refcount == 1) { - throw ActivationError( - "Variable: " + _name + - " did not exist in the context and no default value " - "was given, likely " - "the Set block was in chain that already ended."); - } + // sanity check that the variable is not pristine... + // the setter chain might have stopped actually! + if (_defaultValue.valueType == None && _target->refcount == 1) { + throw ActivationError( + "Variable: " + _name + + " did not exist in the context and no default value " + "was given, likely " + "the Set block was in chain that already ended."); } if (_isTable) { @@ -1132,16 +1169,13 @@ struct Push : public VariableBase { } void destroy() { - if (_firstPusher) { - if (_tableInfo.table.keys.elements) - chainblocks::arrayFree(_tableInfo.table.keys); - if (_tableInfo.table.types.elements) - chainblocks::arrayFree(_tableInfo.table.types); - } + if (_tableInfo.table.keys.elements) + chainblocks::arrayFree(_tableInfo.table.keys); + if (_tableInfo.table.types.elements) + chainblocks::arrayFree(_tableInfo.table.types); } CBTypeInfo compose(const CBInstanceData &data) { - // TODO Add more types if inputs are of diff types const auto updateSeqInfo = [this, &data] { _seqInfo.basicType = Seq; _seqInnerInfo = data.inputType; @@ -1274,6 +1308,209 @@ struct Push : public VariableBase { } }; +struct Sequence : public VariableBase { + bool _clear = true; + bool _firstPusher = true; + ParamVar _types{Var::Enum(BasicTypes::Any, 'sink', 'type')}; + Types _seqTypes{}; + std::deque _innerTypes; + + static inline ParamsInfo pushParams = ParamsInfo( + variableParamsInfo, + ParamsInfo::Param( + "Clear", + "If we should clear this sequence at every chain iteration; works " + "only if this is the first push; default: true.", + CoreInfo::BoolType), + ParamsInfo::Param("Types", "The sequence inner types to forward declare.", + CoreInfo::BasicTypesTypes)); + + static CBParametersInfo parameters() { return CBParametersInfo(pushParams); } + + void setParam(int index, CBVar value) { + if (index <= 2) + VariableBase::setParam(index, value); + else if (index == 3) { + _clear = value.payload.boolValue; + } else if (index == 4) { + _types = value; + } + } + + CBVar getParam(int index) { + if (index <= 2) + return VariableBase::getParam(index); + else if (index == 3) + return Var(_clear); + else if (index == 4) + return _types; + throw CBException("Param index out of range."); + } + + void addType(Types &inner, BasicTypes type) { + switch (type) { + case BasicTypes::Any: + inner._types.emplace_back(CoreInfo::AnyType); + break; + case BasicTypes::Bool: + inner._types.emplace_back(CoreInfo::BoolType); + break; + case BasicTypes::Int: + inner._types.emplace_back(CoreInfo::IntType); + break; + case BasicTypes::Int2: + inner._types.emplace_back(CoreInfo::Int2Type); + break; + case BasicTypes::Int3: + inner._types.emplace_back(CoreInfo::Int3Type); + break; + case BasicTypes::Int4: + inner._types.emplace_back(CoreInfo::Int4Type); + break; + case BasicTypes::Int8: + inner._types.emplace_back(CoreInfo::Int8Type); + break; + case BasicTypes::Int16: + inner._types.emplace_back(CoreInfo::Int16Type); + break; + case BasicTypes::Float: + inner._types.emplace_back(CoreInfo::FloatType); + break; + case BasicTypes::Float2: + inner._types.emplace_back(CoreInfo::Float2Type); + break; + case BasicTypes::Float3: + inner._types.emplace_back(CoreInfo::Float3Type); + break; + case BasicTypes::Float4: + inner._types.emplace_back(CoreInfo::Float4Type); + break; + case BasicTypes::Color: + inner._types.emplace_back(CoreInfo::ColorType); + break; + case BasicTypes::Chain: + inner._types.emplace_back(CoreInfo::ChainType); + break; + case BasicTypes::Block: + inner._types.emplace_back(CoreInfo::BlockType); + break; + case BasicTypes::Bytes: + inner._types.emplace_back(CoreInfo::BytesType); + break; + case BasicTypes::String: + inner._types.emplace_back(CoreInfo::StringType); + break; + case BasicTypes::Image: + inner._types.emplace_back(CoreInfo::ImageType); + break; + } + } + + void processTypes(Types &inner, const IterableSeq &s) { + for (auto &v : s) { + if (v.valueType == Seq) { + auto &sinner = _innerTypes.emplace_back(); + IterableSeq ss(v); + processTypes(sinner, ss); + CBTypeInfo stype{CBType::Seq, {.seqTypes = inner}}; + inner._types.emplace_back(stype); + } else { + const auto type = BasicTypes(v.payload.enumValue); + // assume enum + addType(inner, type); + } + } + } + + CBTypeInfo compose(const CBInstanceData &data) { + // Ensure variable did not exist + for (uint32_t i = 0; i < data.shared.len; i++) { + auto &reference = data.shared.elements[i]; + if (strcmp(reference.name, _name.c_str()) == 0) { + throw ComposeError("Sequence - Variable " + _name + " already exists."); + } + } + + // Process types to expose + // cleaning up previous first + _seqTypes._types.clear(); + _innerTypes.clear(); + if (_types->valueType == Enum) { + // a single type + addType(_seqTypes, BasicTypes(_types->payload.enumValue)); + } else { + IterableSeq st(_types); + processTypes(_seqTypes, st); + } + + CBTypeInfo stype{CBType::Seq, {.seqTypes = _seqTypes}}; + if (_global) { + _exposedInfo = ExposedInfo(ExposedInfo::GlobalVariable( + _name.c_str(), "The exposed sequence.", stype, true)); + } else { + _exposedInfo = ExposedInfo(ExposedInfo::Variable( + _name.c_str(), "The exposed sequence.", stype, true)); + } + + return data.inputType; + } + + CBExposedTypesInfo exposedVariables() { + return CBExposedTypesInfo(_exposedInfo); + } + + static CBTypesInfo inputTypes() { return CoreInfo::AnyType; } + + static CBTypesInfo outputTypes() { return CoreInfo::AnyType; } + + void warmup(CBContext *context) { + if (_global) + _target = referenceGlobalVariable(context, _name.c_str()); + else + _target = referenceVariable(context, _name.c_str()); + } + + void activateTable(CBContext *context, const CBVar &input) { + if (_target->valueType != Table) { + // Not initialized yet + _target->valueType = Table; + _target->payload.tableValue.api = &Globals::TableInterface; + _target->payload.tableValue.opaque = new CBMap(); + } + + if (unlikely(_cell == nullptr)) { + _cell = _target->payload.tableValue.api->tableAt( + _target->payload.tableValue, _key.c_str()); + } + auto &seq = *_cell; + + if (seq.valueType != Seq) { + seq.valueType = Seq; + seq.payload.seqValue = {}; + } + + if (_firstPusher && _clear) { + chainblocks::arrayResize(seq.payload.seqValue, 0); + } + } + + ALWAYS_INLINE CBVar activate(CBContext *context, const CBVar &input) { + if (unlikely(_isTable)) { + activateTable(context, input); + } else { + if (_target->valueType != Seq) { + _target->valueType = Seq; + _target->payload.seqValue = {}; + } + + if (_firstPusher && _clear) { + chainblocks::arrayResize(_target->payload.seqValue, 0); + } + } + return input; + } +}; + struct SeqUser : VariableBase { bool _blittable = false; @@ -1848,7 +2085,7 @@ struct Take { default: break; } - return Empty; + return Var::Empty; } struct OutOfRangeEx : public ActivationError { @@ -2181,7 +2418,7 @@ struct Slice { default: break; } - return Empty; + return Var::Empty; } struct OutOfRangeEx : public ActivationError { @@ -2301,7 +2538,6 @@ struct Repeat { int64_t _times = 0; bool _forever = false; ExposedInfo _requiredInfo{}; - CBValidationResult _validation{}; void cleanup() { if (_ctxTimes) { @@ -2383,7 +2619,7 @@ struct Repeat { } CBTypeInfo compose(const CBInstanceData &data) { - _validation = _blks.compose(data); + _blks.compose(data); const auto predres = _pred.compose(data); if (_pred && predres.outputType.basicType != Bool) { throw ComposeError( @@ -2403,8 +2639,6 @@ struct Repeat { } } - CBExposedTypesInfo exposedVariables() { return _validation.exposedInfo; } - ALWAYS_INLINE CBVar activate(CBContext *context, const CBVar &input) { auto repeats = _forever ? 1 : _times; @@ -2416,19 +2650,20 @@ struct Repeat { while (repeats) { CBVar repeatOutput{}; - repeatOutput.valueType = None; - repeatOutput.payload.chainState = CBChainState::Continue; CBVar blks = _blks; - if (!activateBlocks(blks.payload.seqValue, context, input, repeatOutput)) - break; // this is a Return signal + auto state = activateBlocks(blks.payload.seqValue, context, input, + repeatOutput, true); + if (state != CBChainState::Continue) + break; if (!_forever) repeats--; if (_pred) { - const auto pres = _pred.activate(context, input); - // no type check, done in compose! - if (pres.payload.boolValue) + CBVar pres{}; + state = _pred.activate(context, input, pres, true); + // logic flow here! + if (pres.payload.boolValue || state > CBChainState::Return) break; } } @@ -2516,6 +2751,7 @@ RUNTIME_CORE_BLOCK_TYPE(RTake); RUNTIME_CORE_BLOCK_TYPE(Slice); RUNTIME_CORE_BLOCK_TYPE(Limit); RUNTIME_CORE_BLOCK_TYPE(Push); +RUNTIME_CORE_BLOCK_TYPE(Sequence); RUNTIME_CORE_BLOCK_TYPE(Pop); RUNTIME_CORE_BLOCK_TYPE(PopFront); RUNTIME_CORE_BLOCK_TYPE(Clear); diff --git a/src/core/blocks/flow.cpp b/src/core/blocks/flow.cpp index 93473e06a7c..b2d5c760c52 100644 --- a/src/core/blocks/flow.cpp +++ b/src/core/blocks/flow.cpp @@ -277,19 +277,23 @@ struct Cond { auto idx = 0; CBVar actionInput = input; - CBVar finalOutput = RestartChain; + CBVar finalOutput{}; for (auto &cond : _conditions) { CBVar output{}; CBlocks blocks{&cond[0], (uint32_t)cond.size(), 0}; - activateBlocks(blocks, context, input, output); - if (output == True) { + auto state = activateBlocks(blocks, context, input, output, true); + // conditional flow so we might have "returns" form (And) (Or) + if (state > CBChainState::Return) + return output; + + if (output == Var::True) { // Do the action if true! // And stop here output = {}; CBlocks action{&_actions[idx][0], (uint32_t)_actions[idx].size(), 0}; - if (!activateBlocks(action, context, actionInput, output)) - return Var::Return(); // we need to propagate Return! - + state = activateBlocks(action, context, actionInput, output); + if (state != CBChainState::Continue) + return output; if (_threading) { // set the output as the next action input (not cond tho!) finalOutput = output; @@ -360,20 +364,18 @@ struct BaseSubFlow { struct MaybeRestart : public BaseSubFlow { CBVar activate(CBContext *context, const CBVar &input) { + CBVar output{}; if (likely(_blocks)) { try { - return _blocks.activate(context, input); - } catch (const ChainCancellation &) { - return Var::Stop(); + _blocks.activate(context, input, output); } catch (const ActivationError &ex) { if (ex.triggerFailure()) { LOG(WARNING) << "Maybe block Ignored a failure: " << ex.what(); } - return Var::Restart(); + context->restartFlow(input); } - } else { - return {}; } + return output; } }; @@ -412,30 +414,8 @@ struct Maybe : public BaseSubFlow { "Maybe: output types mismatch between the two possible flows!"); } - auto exposing = true; - auto curlen = elseComp.exposedInfo.len; - auto newlen = _composition.exposedInfo.len; - if (curlen != newlen) { - LOG(INFO) << "Maybe: number of exposed variables between actions " - "mismatch, " - "variables won't be exposed, flow is unpredictable!"; - exposing = false; - } - - if (exposing) { - for (uint32_t i = 0; i < curlen; i++) { - if (elseComp.exposedInfo.elements[i] != - _composition.exposedInfo.elements[i]) { - LOG(INFO) << "Maybe: types of exposed variables between actions " - "mismatch, " - "variables won't be exposed, flow is unpredictable!"; - exposing = false; - break; - } - } - } - if (!exposing) - _composition.exposedInfo = {}; + // Maybe won't expose + _composition.exposedInfo = {}; return _composition.outputType; } @@ -455,22 +435,18 @@ struct Maybe : public BaseSubFlow { } CBVar activate(CBContext *context, const CBVar &input) { + CBVar output{}; if (likely(_blocks)) { try { - return _blocks.activate(context, input); - } catch (const ChainCancellation &) { - return Var::Stop(); - } catch (const ChainRestart &) { - return Var::Restart(); + _blocks.activate(context, input, output); } catch (const ActivationError &ex) { if (ex.triggerFailure()) { LOG(WARNING) << "Maybe block Ignored a failure: " << ex.what(); } - return _elseBlks.activate(context, input); + _elseBlks.activate(context, input, output); } - } else { - return {}; } + return output; } private: @@ -489,19 +465,19 @@ struct Await : public BaseSubFlow { CBVar getParam(int index) { return _blocks; } CBVar activate(CBContext *context, const CBVar &input) { + CBVar output{}; if (likely(_blocks)) { // avoid nesting, if we are alrady inside a worker // run normally if (Tasks.this_worker_id() == -1) { AsyncOp op(context); - return op.sidechain( - Tasks, [&]() { return _blocks.activate(context, input); }); + op.sidechain(Tasks, + [&]() { _blocks.activate(context, input, output); }); } else { - return _blocks.activate(context, input); + _blocks.activate(context, input, output); } - } else { - return {}; } + return output; } private: @@ -570,8 +546,7 @@ template struct When { auto ares = _action.compose(data); - _shouldReturn = ares.flowStopper; - if (!_shouldReturn && !_passth) { + if (!ares.flowStopper && !_passth) { if (cres.outputType != data.inputType) { throw ComposeError("When Passthrough is false but action output type " "does not match input type."); @@ -591,14 +566,16 @@ template struct When { } CBVar activate(CBContext *context, const CBVar &input) { - const auto cres = _cond.activate(context, input); + CBVar output{}; + auto state = _cond.activate(context, input, output, true); + if (state > CBChainState::Return) + return input; + // type check in compose! - if (cres.payload.boolValue == COND) { - const auto ares = _action.activate(context, input); - if (_shouldReturn) - return Var::Return(); + if (output.payload.boolValue == COND) { + _action.activate(context, input, output); if (!_passth) - return ares; + return output; } return input; } @@ -618,7 +595,6 @@ template struct When { BlocksVar _cond{}; BlocksVar _action{}; bool _passth = true; - bool _shouldReturn = false; }; struct IfBlock { @@ -659,17 +635,7 @@ struct IfBlock { const auto tres = _then.compose(data); const auto eres = _else.compose(data); - // if exposes nothing, so this is not needed here!!! - // if ((tres.flowStopper && !eres.flowStopper) || - // (!tres.flowStopper && eres.flowStopper)) { - // throw ComposeError( - // "If - actions unbalance, one stops the flow other does not."); - // } - - _tshouldReturn = tres.flowStopper; - _eshouldReturn = eres.flowStopper; - - if (!_tshouldReturn && !_eshouldReturn && !_passth) { + if (!tres.flowStopper && !eres.flowStopper && !_passth) { if (tres.outputType != eres.outputType) { throw ComposeError("If - Passthrough is false but action output types " "do not match."); @@ -691,22 +657,22 @@ struct IfBlock { } CBVar activate(CBContext *context, const CBVar &input) { - const auto cres = _cond.activate(context, input); + CBVar output{}; + auto state = _cond.activate(context, input, output, true); + if (state > CBChainState::Return) + return input; + // type check in compose! - if (cres.payload.boolValue) { - const auto tres = _then.activate(context, input); - if (_tshouldReturn) - return Var::Return(); - if (!_passth) - return tres; + if (output.payload.boolValue) { + _then.activate(context, input, output); } else { - const auto eres = _else.activate(context, input); - if (_eshouldReturn) - return Var::Return(); - if (!_passth) - return eres; + _else.activate(context, input, output); } - return input; + + if (!_passth) + return output; + else + return input; } private: @@ -725,8 +691,6 @@ struct IfBlock { BlocksVar _then{}; BlocksVar _else{}; bool _passth = false; - bool _tshouldReturn = false; - bool _eshouldReturn = false; }; void registerFlowBlocks() { diff --git a/src/core/blocks/fs.cpp b/src/core/blocks/fs.cpp index 8393037db87..68c0d9ac4d9 100644 --- a/src/core/blocks/fs.cpp +++ b/src/core/blocks/fs.cpp @@ -42,7 +42,7 @@ struct Iterate { case 0: return Var(_recursive); default: - return Empty; + return Var::Empty; } } @@ -133,9 +133,9 @@ struct IsFile { CBVar activate(CBContext *context, const CBVar &input) { fs::path p(input.payload.stringValue); if (fs::exists(p) && !fs::is_directory(p)) { - return True; + return Var::True; } else { - return False; + return Var::False; } } }; @@ -152,9 +152,9 @@ struct IsDirectory { CBVar activate(CBContext *context, const CBVar &input) { fs::path p(input.payload.stringValue); if (fs::exists(p) && fs::is_directory(p)) { - return True; + return Var::True; } else { - return False; + return Var::False; } } }; @@ -190,7 +190,7 @@ struct Filename { case 0: return Var(_noExt); default: - return Empty; + return Var::Empty; } } @@ -245,7 +245,7 @@ struct Read { case 0: return Var(_binary); default: - return Empty; + return Var::Empty; } } @@ -320,7 +320,7 @@ struct Write { case 2: return Var(_append); default: - return Empty; + return Var::Empty; } } diff --git a/src/core/blocks/genetic.cpp b/src/core/blocks/genetic.cpp index 4e16f4dc5e3..f2357b68956 100644 --- a/src/core/blocks/genetic.cpp +++ b/src/core/blocks/genetic.cpp @@ -278,7 +278,7 @@ struct Evolve { auto fitchain = CBChain::sharedFromRef(i.fitnessChain.payload.chainValue); auto chain = CBChain::sharedFromRef(i.chain.payload.chainValue); - i.node.schedule(obs, fitchain.get(), chain->previousOutput); + i.node.schedule(obs, fitchain.get(), chain->finishedOutput); }, _coros); @@ -702,7 +702,9 @@ struct Mutant { } CBVar activate(CBContext *context, const CBVar &input) { - return _block.activate(context, input); + CBVar output{}; + _block.activate(context, input, output); + return output; } CBlock *mutant() const { diff --git a/src/core/blocks/http.cpp b/src/core/blocks/http.cpp index 9ed849fa4c5..80d6b76edf0 100644 --- a/src/core/blocks/http.cpp +++ b/src/core/blocks/http.cpp @@ -137,8 +137,6 @@ struct Client { connected = true; }); - } catch (ChainCancellation &) { - throw; } catch (std::exception &ex) { // TODO some exceptions could be left unhandled // or anyway should be fatal @@ -241,8 +239,6 @@ struct Get final : public Client { // Receive the HTTP response http::read(stream, buffer, res); }); - } catch (ChainCancellation &) { - throw; } catch (std::exception &ex) { // TODO some exceptions could be left unhandled // or anyway should be fatal @@ -294,8 +290,6 @@ struct Post final : public Client { // Receive the HTTP response http::read(stream, buffer, res); }); - } catch (ChainCancellation &) { - throw; } catch (std::exception &ex) { // TODO some exceptions could be left unhandled // or anyway should be fatal diff --git a/src/core/blocks/json.cpp b/src/core/blocks/json.cpp index ea9d748c783..fa268304198 100644 --- a/src/core/blocks/json.cpp +++ b/src/core/blocks/json.cpp @@ -59,7 +59,7 @@ void to_json(json &j, const CBVar &var) { break; } case None: { - j = json{{"type", 0}, {"value", int(var.payload.chainState)}}; + j = json{{"type", 0}}; break; } case Bool: { @@ -256,7 +256,6 @@ void from_json(const json &j, CBVar &var) { } case None: { var.valueType = None; - var.payload.chainState = CBChainState(j.at("value").get()); break; } case Bool: { diff --git a/src/core/blocks/network.cpp b/src/core/blocks/network.cpp index 22957ddda91..1b1e7e5ec00 100644 --- a/src/core/blocks/network.cpp +++ b/src/core/blocks/network.cpp @@ -158,7 +158,7 @@ struct NetworkBase { case 2: return _blks; default: - return Empty; + return Var::Empty; } } diff --git a/src/core/blocks/serialization.cpp b/src/core/blocks/serialization.cpp index 6e3e35245cf..a0c213af358 100644 --- a/src/core/blocks/serialization.cpp +++ b/src/core/blocks/serialization.cpp @@ -160,7 +160,7 @@ struct ReadFile : public FileBase { void cleanup() override { Serialization::varFree(_output); FileBase::cleanup(); - if (_fileStream.good() && _fileStream.is_open()) { + if (_fileStream.is_open()) { _fileStream.close(); } } diff --git a/src/core/foundation.hpp b/src/core/foundation.hpp index cd938ce9154..994ef32b460 100644 --- a/src/core/foundation.hpp +++ b/src/core/foundation.hpp @@ -25,6 +25,7 @@ const unsigned __tsan_switch_to_fiber_no_sync = 1 << 0; #include #include #include +#include #include #include #include @@ -48,14 +49,16 @@ CBValidationResult validateConnections(const CBlocks chain, namespace chainblocks { constexpr uint32_t FragCC = 'frag'; // 1718772071 -bool activateBlocks(CBSeq blocks, CBContext *context, const CBVar &chainInput, - CBVar &output); -bool activateBlocks(CBlocks blocks, CBContext *context, const CBVar &chainInput, - CBVar &output); +CBChainState activateBlocks(CBSeq blocks, CBContext *context, + const CBVar &chainInput, CBVar &output, + const bool handlesReturn = false); +CBChainState activateBlocks(CBlocks blocks, CBContext *context, + const CBVar &chainInput, CBVar &output, + const bool handlesReturn = false); CBVar *referenceGlobalVariable(CBContext *ctx, const char *name); CBVar *referenceVariable(CBContext *ctx, const char *name); void releaseVariable(CBVar *variable); -void suspend(CBContext *context, double seconds); +CBChainState suspend(CBContext *context, double seconds); void registerEnumType(int32_t vendorId, int32_t enumId, CBEnumInfo info); CBlock *createBlock(const char *name); @@ -230,14 +233,6 @@ struct Globals { static inline std::string RootPath; - constexpr static CBVar True = Var::True(); - constexpr static CBVar False = Var::False(); - constexpr static CBVar StopChain = Var::Stop(); - constexpr static CBVar RestartChain = Var::Restart(); - constexpr static CBVar ReturnPrevious = Var::Return(); - constexpr static CBVar RebaseChain = Var::Rebase(); - constexpr static CBVar Empty = Var::Empty(); - static inline CBTableInterface TableInterface{ .tableForEach = [](CBTable table, CBTableForEachCallback cb, void *data) { @@ -287,14 +282,6 @@ struct Globals { }}; }; -#define True ::chainblocks::Globals::True -#define False ::chainblocks::Globals::False -#define StopChain ::chainblocks::Globals::StopChain -#define RestartChain ::chainblocks::Globals::RestartChain -#define ReturnPrevious ::chainblocks::Globals::ReturnPrevious -#define RebaseChain ::chainblocks::Globals::RebaseChain -#define Empty ::chainblocks::Globals::Empty - template NO_INLINE void arrayGrow(T &arr, size_t addlen, size_t min_cap = 4); @@ -578,6 +565,7 @@ ALWAYS_INLINE inline void destroyVar(CBVar &var) { break; }; + memset(&var.payload, 0x0, sizeof(CBVarPayload)); var.valueType = CBType::None; } @@ -1255,12 +1243,6 @@ struct InternalCore { throw chainblocks::CBException(msg); } - [[noreturn]] static void throwCancellation() { - throw chainblocks::ChainCancellation(); - } - - [[noreturn]] static void throwRestart() { throw chainblocks::ChainRestart(); } - static void log(const char *msg) { LOG(INFO) << msg; } static void registerEnumType(int32_t vendorId, int32_t enumId, @@ -1275,21 +1257,14 @@ struct InternalCore { return validateConnections(blocks, callback, userData, data); } - static CBVar runBlocks(CBlocks blocks, CBContext *context, CBVar input) { - CBVar output{}; - chainblocks::activateBlocks(blocks, context, input, output); - return output; + static CBChainState runBlocks(CBlocks blocks, CBContext *context, CBVar input, + CBVar *output, const CBBool handleReturn) { + return chainblocks::activateBlocks(blocks, context, input, *output, + handleReturn); } - static CBVar suspend(CBContext *ctx, double seconds) { - try { - chainblocks::suspend(ctx, seconds); - } catch (const ChainRestart &) { - return Var::Restart(); - } catch (const ChainCancellation &) { - return Var::Stop(); - } - return Empty; + static CBChainState suspend(CBContext *ctx, double seconds) { + return chainblocks::suspend(ctx, seconds); } }; diff --git a/src/core/ops_internal.hpp b/src/core/ops_internal.hpp index dfb010a7f0f..1a3f3059888 100644 --- a/src/core/ops_internal.hpp +++ b/src/core/ops_internal.hpp @@ -12,16 +12,7 @@ inline MAKE_LOGGABLE(CBVar, var, os) { case EndOfBlittableTypes: break; case None: - if (var.payload.chainState == Continue) - os << "*None*"; - else if (var.payload.chainState == CBChainState::Stop) - os << "*ChainStop*"; - else if (var.payload.chainState == CBChainState::Restart) - os << "*ChainRestart*"; - else if (var.payload.chainState == CBChainState::Return) - os << "*ChainReturn*"; - else if (var.payload.chainState == CBChainState::Rebase) - os << "*ChainRebase*"; + os << "*None*"; break; case CBType::Any: os << "*Any*"; diff --git a/src/core/runtime.cpp b/src/core/runtime.cpp index 58b1e7b2d4e..09fcebb183f 100644 --- a/src/core/runtime.cpp +++ b/src/core/runtime.cpp @@ -552,7 +552,7 @@ void releaseVariable(CBVar *variable) { } } -void suspend(CBContext *context, double seconds) { +CBChainState suspend(CBContext *context, double seconds) { if (!context->continuation) { throw ActivationError("Trying to suspend a context without coroutine!", CBChainState::Stop, true); @@ -571,51 +571,45 @@ void suspend(CBContext *context, double seconds) { #ifdef CB_USE_TSAN __tsan_switch_to_fiber(curr, 0); #endif - if (context->restarted) { - throw ChainRestart(); - } else if (context->aborted) { - throw ChainCancellation(); - } + return context->getState(); } -bool activateBlocks(CBlocks blocks, CBContext *context, const CBVar &chainInput, - CBVar &output) { +CBChainState activateBlocks(CBlocks blocks, CBContext *context, + const CBVar &chainInput, CBVar &output, + const bool handlesReturn) { auto input = chainInput; // validation prevents extra pops so this should be safe auto sidx = context->stack.len; DEFER({ context->stack.len = sidx; }); for (uint32_t i = 0; i < blocks.len; i++) { output = activateBlock(blocks.elements[i], context, input); - if (output.valueType == None) { - switch (output.payload.chainState) { - case CBChainState::Restart: { - throw ChainRestart(); - } - case CBChainState::Stop: { - throw ChainStop(); - } - case CBChainState::Return: { - // Invert them, we return previous output (input) - output = input; - // falso on partial run! - return false; - } - case CBChainState::Rebase: { + if (!context->shouldContinue()) { + switch (context->getState()) { + case CBChainState::Return: + if (handlesReturn) + context->continueFlow(); + return CBChainState::Return; + case CBChainState::Stop: + case CBChainState::Restart: + return context->getState(); + case CBChainState::Rebase: + // reset input to chain one + // and reset state input = chainInput; + context->continueFlow(); continue; - } - case Continue: + case CBChainState::Continue: break; } } input = output; } - // true on full run! - return true; + return CBChainState::Continue; } -bool activateBlocks(CBSeq blocks, CBContext *context, const CBVar &chainInput, - CBVar &output) { +CBChainState activateBlocks(CBSeq blocks, CBContext *context, + const CBVar &chainInput, CBVar &output, + const bool handlesReturn) { auto input = chainInput; // validation prevents extra pops so this should be safe auto sidx = context->stack.len; @@ -623,32 +617,28 @@ bool activateBlocks(CBSeq blocks, CBContext *context, const CBVar &chainInput, for (uint32_t i = 0; i < blocks.len; i++) { output = activateBlock(blocks.elements[i].payload.blockValue, context, input); - if (output.valueType == None) { - switch (output.payload.chainState) { - case CBChainState::Restart: { - throw ChainRestart(); - } - case CBChainState::Stop: { - throw ChainStop(); - } - case CBChainState::Return: { - // Invert them, we return previous output (input) - output = input; - // falso on partial run! - return false; - } - case CBChainState::Rebase: { + if (!context->shouldContinue()) { + switch (context->getState()) { + case CBChainState::Return: + if (handlesReturn) + context->continueFlow(); + return CBChainState::Return; + case CBChainState::Stop: + case CBChainState::Restart: + return context->getState(); + case CBChainState::Rebase: + // reset input to chain one + // and reset state input = chainInput; + context->continueFlow(); continue; - } - case Continue: + case CBChainState::Continue: break; } } input = output; } - // true on full run! - return true; + return CBChainState::Continue; } CBSeq *InternalCore::getStack(CBContext *context) { return &context->stack; } @@ -657,14 +647,6 @@ CBSeq *InternalCore::getStack(CBContext *context) { return &context->stack; } throwException(const char *msg) { throw CBException(msg); } - -[[noreturn]] __attribute__((noreturn)) static void throwCancellation() { - throw ChainCancellation(); -} - -[[noreturn]] __attribute__((noreturn)) static void throwRestart() { - throw ChainRestart(); -} }; // namespace chainblocks #ifndef OVERRIDE_REGISTER_ALL_BLOCKS @@ -727,19 +709,8 @@ EXPORTED struct CBCore __cdecl chainblocksInterface(uint32_t abi_version) { result.throwException = &chainblocks::throwException; - result.throwCancellation = &chainblocks::throwCancellation; - - result.throwRestart = &chainblocks::throwRestart; - result.suspend = [](CBContext *context, double seconds) { - try { - chainblocks::suspend(context, seconds); - } catch (const chainblocks::ChainRestart &) { - return CBVar{{CBChainState::Restart}}; - } catch (const chainblocks::ChainCancellation &) { - return CBVar{{CBChainState::Stop}}; - } - return CBVar{{CBChainState::Continue}}; + return chainblocks::suspend(context, seconds); }; result.cloneVar = [](CBVar *dst, const CBVar *src) { @@ -808,10 +779,10 @@ EXPORTED struct CBCore __cdecl chainblocksInterface(uint32_t abi_version) { return validateSetParam(block, index, param, callback, userData); }; - result.runBlocks = [](CBlocks blocks, CBContext *context, CBVar input) { - CBVar output{}; - chainblocks::activateBlocks(blocks, context, input, output); - return output; + result.runBlocks = [](CBlocks blocks, CBContext *context, CBVar input, + CBVar *output, const CBBool handleReturn) { + return chainblocks::activateBlocks(blocks, context, input, *output, + handleReturn); }; result.getChainInfo = [](CBChainRef chainref) { @@ -1563,21 +1534,19 @@ CBRunChainOutput runChain(CBChain *chain, CBContext *context, for (auto blk : chain->blocks) { try { chain->previousOutput = activateBlock(blk, context, input); - if (chain->previousOutput.valueType == None) { - switch (chain->previousOutput.payload.chainState) { + if (!context->shouldContinue()) { + switch (context->getState()) { + case CBChainState::Return: case CBChainState::Restart: { - return {input, Restarted}; + return {context->getFlowStorage(), Restarted}; } case CBChainState::Stop: { - return {input, Stopped}; - } - case CBChainState::Return: { - // Use input as output, return previous block result - return {input, Restarted}; + return {context->getFlowStorage(), Stopped}; } case CBChainState::Rebase: // Rebase means we need to put back main input input = chainInput; + context->continueFlow(); continue; case CBChainState::Continue: break; @@ -1593,19 +1562,17 @@ CBRunChainOutput runChain(CBChain *chain, CBContext *context, return {input, Failed}; } else { switch (e.requestedAction()) { + case CBChainState::Return: case CBChainState::Restart: { return {input, Restarted}; } case CBChainState::Stop: { return {input, Stopped}; } - case CBChainState::Return: { - // Use input as output, return previous block result - return {input, Restarted}; - } case CBChainState::Rebase: // Rebase means we need to put back main input input = chainInput; + context->continueFlow(); continue; case CBChainState::Continue: break; @@ -1672,17 +1639,18 @@ boost::context::continuation run(CBChain *chain, // And call warmup on all the blocks! if (!warmup(chain, &context)) { chain->state = CBChain::State::Failed; - context.aborted = true; + context.stopFlow(Var::Empty); goto endOfChain; } context.continuation = context.continuation.resume(); - if (context.aborted) // We might have stopped before even starting! + if (context.shouldStop()) // We might have stopped before even starting! goto endOfChain; while (running) { running = chain->looped; - context.restarted = false; // Remove restarted flag + // reset context state + context.continueFlow(); auto runRes = runChain(chain, &context, chain->rootTickInput); chain->finishedOutput = runRes.output; // Write result before setting flag @@ -1691,11 +1659,11 @@ boost::context::continuation run(CBChain *chain, if (unlikely(runRes.state == Failed)) { LOG(DEBUG) << "chain " << chain->name << " failed."; chain->state = CBChain::State::Failed; - context.aborted = true; + context.stopFlow(Var::Empty); break; } else if (unlikely(runRes.state == Stopped)) { LOG(DEBUG) << "chain " << chain->name << " stopped."; - context.aborted = true; + context.stopFlow(Var::Empty); break; } @@ -1704,7 +1672,7 @@ boost::context::continuation run(CBChain *chain, context.next = Duration(0); context.continuation = context.continuation.resume(); // This is delayed upon continuation!! - if (context.aborted) { + if (context.shouldStop()) { LOG(DEBUG) << "chain " << chain->name << " aborted on resume."; break; } diff --git a/src/core/runtime.hpp b/src/core/runtime.hpp index fe730bbae4d..c6501176968 100644 --- a/src/core/runtime.hpp +++ b/src/core/runtime.hpp @@ -65,8 +65,7 @@ bool validateSetParam(CBlock *block, int index, CBVar &value, struct CBContext { CBContext(CBCoro &&sink, CBChain *starter) - : main(starter), restarted(false), aborted(false), - continuation(std::move(sink)), iterationCount(0), stack({}) { + : main(starter), continuation(std::move(sink)) { chainStack.push_back(starter); } @@ -75,23 +74,55 @@ struct CBContext { const CBChain *main; std::vector chainStack; - // Those 2 go together with CBVar chainstates restart and stop - bool restarted; - // Also used to cancel a chain - bool aborted; - // Used within the coro& stack! (suspend, etc) CBCoro &&continuation; Duration next{}; #ifdef CB_USE_TSAN - void *tsan_handle; + void *tsan_handle = nullptr; #endif // Iteration counter - uint64_t iterationCount; + uint64_t iterationCount = 0; // Stack for local vars - CBSeq stack; + CBSeq stack{}; + + constexpr void stopFlow(const CBVar &lastValue) { + state = CBChainState::Stop; + flowStorage = lastValue; + } + + constexpr void restartFlow(const CBVar &lastValue) { + state = CBChainState::Restart; + flowStorage = lastValue; + } + + constexpr void returnFlow(const CBVar &lastValue) { + state = CBChainState::Return; + flowStorage = lastValue; + } + + constexpr void rebaseFlow() { state = CBChainState::Rebase; } + + constexpr void continueFlow() { state = CBChainState::Continue; } + + constexpr bool shouldContinue() const { + return state == CBChainState::Continue; + } + + constexpr bool shouldReturn() const { return state == CBChainState::Return; } + + constexpr bool shouldStop() const { return state == CBChainState::Stop; } + + constexpr CBChainState getState() const { return state; } + + constexpr CBVar getFlowStorage() const { return flowStorage; } + +private: + CBChainState state = CBChainState::Continue; + // Used when flow is stopped/restart/return + // to store the previous result + CBVar flowStorage{}; }; #include "blocks/core.hpp" @@ -480,7 +511,7 @@ inline bool stop(CBChain *chain, CBVar *result = nullptr) { if ((*chain->coro) && chain->state > CBChain::State::Stopped && chain->state < CBChain::State::Failed) { // set abortion flag, we always have a context in this case - chain->context->aborted = true; + chain->context->stopFlow(Var::Empty); // BIG Warning: chain->context existed in the coro stack!!! // after this resume chain->context is trash! @@ -521,7 +552,7 @@ inline bool tick(CBChain *chain, CBVar rootInput = {}) { Duration now = Clock::now().time_since_epoch(); if (now >= chain->context->next) { - if (rootInput != Empty) { + if (rootInput != Var::Empty) { cloneVar(chain->rootTickInput, rootInput); } #ifdef CB_USE_TSAN @@ -540,7 +571,7 @@ inline bool hasEnded(CBChain *chain) { return chain->state > CBChain::State::IterationEnded; } -inline bool isCanceled(CBContext *context) { return context->aborted; } +inline bool isCanceled(CBContext *context) { return context->shouldStop(); } inline void sleep(double seconds = -1.0, bool runCallbacks = true) { // negative = no sleep, just run callbacks @@ -661,7 +692,8 @@ struct CBNode { schedule(obs, chain, input, validate); } - template bool tick(Observer observer, CBVar input = Empty) { + template + bool tick(Observer observer, CBVar input = chainblocks::Var::Empty) { auto noErrors = true; _runningFlows = flows; for (auto &flow : _runningFlows) { @@ -685,7 +717,7 @@ struct CBNode { return noErrors; } - bool tick(CBVar input = Empty) { + bool tick(CBVar input = chainblocks::Var::Empty) { EmptyObserver obs; return tick(obs, input); } diff --git a/src/extra/bgfx.cpp b/src/extra/bgfx.cpp index c2d36136c9c..11d9cf0fdb1 100644 --- a/src/extra/bgfx.cpp +++ b/src/extra/bgfx.cpp @@ -92,7 +92,7 @@ struct BaseWindow : public Base { case 2: return Var(_height); default: - return Empty; + return Var::Empty; } } }; diff --git a/src/extra/desktop.hpp b/src/extra/desktop.hpp index fb28665a947..efa595416c9 100644 --- a/src/extra/desktop.hpp +++ b/src/extra/desktop.hpp @@ -316,7 +316,7 @@ struct SendKeyEventBase { CBVar getParam(int index) { if (_windowVarName.size() == 0) { - return Empty; + return Var::Empty; } else { return Var(_windowVarName); } diff --git a/src/extra/desktop.win.cpp b/src/extra/desktop.win.cpp index a358b57809b..f30a959bd40 100644 --- a/src/extra/desktop.win.cpp +++ b/src/extra/desktop.win.cpp @@ -64,7 +64,7 @@ class HasWindow : public WindowWindows { } } - return _window ? True : False; + return _window ? Var::True : Var::False; } }; @@ -247,7 +247,7 @@ struct IsForeground : public ActiveBase { auto hwnd = AsHWND(input); if (hwnd) { auto currentForeground = GetForegroundWindow(); - return hwnd == currentForeground ? True : False; + return hwnd == currentForeground ? Var::True : Var::False; } else { throw ActivationError("Input object was not a Desktop's window!"); } @@ -284,7 +284,7 @@ struct NotForeground : public ActiveBase { auto hwnd = AsHWND(input); if (hwnd) { auto currentForeground = GetForegroundWindow(); - return hwnd != currentForeground ? True : False; + return hwnd != currentForeground ? Var::True : Var::False; } else { throw ActivationError("Input object was not a Desktop's window!"); } @@ -508,7 +508,7 @@ struct PixelBase { switch (index) { case 0: if (variableName.size() == 0) { - return Empty; + return Var::Empty; } else { CBVar v{}; v.valueType = CBType::ContextVar; diff --git a/src/extra/imgui.cpp b/src/extra/imgui.cpp index d866baf05bc..06a9050c5d5 100644 --- a/src/extra/imgui.cpp +++ b/src/extra/imgui.cpp @@ -144,7 +144,7 @@ struct Style : public Base { case 0: return Var::Enum(_key, 'frag', 'ImGS'); default: - return Empty; + return Var::Empty; } } @@ -599,7 +599,7 @@ struct Window : public Base { case 5: return _blks; default: - return Empty; + return Var::Empty; } } @@ -744,11 +744,11 @@ template struct Variable : public Base { CBVar getParam(int index) { switch (index) { case 0: - return _label.size() == 0 ? Empty : Var(_label); + return _label.size() == 0 ? Var::Empty : Var(_label); case 1: - return _variable_name.size() == 0 ? Empty : Var(_variable_name); + return _variable_name.size() == 0 ? Var::Empty : Var(_variable_name); default: - return Empty; + return Var::Empty; } } }; @@ -767,12 +767,12 @@ struct CheckBox : public Variable { if (_variable) { ::ImGui::Checkbox(_label.c_str(), &_variable->payload.boolValue); - return _variable->payload.boolValue ? True : False; + return _variable->payload.boolValue ? Var::True : Var::False; } else { // HACK kinda... we recycle _exposing since we are not using it in this // branch ::ImGui::Checkbox(_label.c_str(), &_exposing); - return _exposing ? True : False; + return _exposing ? Var::True : Var::False; } } }; @@ -806,11 +806,11 @@ struct Text : public Base { CBVar getParam(int index) { switch (index) { case 0: - return _label.size() == 0 ? Empty : Var(_label); + return _label.size() == 0 ? Var::Empty : Var(_label); case 1: return _color; default: - return Empty; + return Var::Empty; } } @@ -906,7 +906,7 @@ struct Button : public Base { case 3: return Var(_size.x, _size.x); default: - return Empty; + return Var::Empty; } } @@ -923,56 +923,56 @@ struct Button : public Base { #define IMBTN_RUN_ACTION \ { \ - CBVar output = Empty; \ + CBVar output = Var::Empty; \ activateBlocks(CBVar(_blks).payload.seqValue, context, input, output); \ } CBVar activate(CBContext *context, const CBVar &input) { IDContext idCtx(this); - auto result = False; + auto result = Var::False; ImVec2 size; switch (_type) { case Normal: if (::ImGui::Button(_label.c_str(), _size)) { IMBTN_RUN_ACTION; - result = True; + result = Var::True; } break; case Small: if (::ImGui::SmallButton(_label.c_str())) { IMBTN_RUN_ACTION; - result = True; + result = Var::True; } break; case Invisible: if (::ImGui::InvisibleButton(_label.c_str(), _size)) { IMBTN_RUN_ACTION; - result = True; + result = Var::True; } break; case ArrowLeft: if (::ImGui::ArrowButton(_label.c_str(), ImGuiDir_Left)) { IMBTN_RUN_ACTION; - result = True; + result = Var::True; } break; case ArrowRight: if (::ImGui::ArrowButton(_label.c_str(), ImGuiDir_Right)) { IMBTN_RUN_ACTION; - result = True; + result = Var::True; } break; case ArrowUp: if (::ImGui::ArrowButton(_label.c_str(), ImGuiDir_Up)) { IMBTN_RUN_ACTION; - result = True; + result = Var::True; } break; case ArrowDown: if (::ImGui::ArrowButton(_label.c_str(), ImGuiDir_Down)) { IMBTN_RUN_ACTION; - result = True; + result = Var::True; } break; } @@ -1091,9 +1091,9 @@ struct TreeNode : public Base { auto visible = ::ImGui::TreeNode(_label.c_str()); if (visible) { + CBVar output{}; // run inner blocks - _blocks.activate(context, input); - + _blocks.activate(context, input, output); // pop the node if was visible ::ImGui::TreePop(); } @@ -1348,7 +1348,7 @@ struct Image : public Base { case 1: return Var(_trueSize); default: - return Empty; + return Var::Empty; } } diff --git a/src/mal/CBCore.cpp b/src/mal/CBCore.cpp index f10a677725e..b40496d78b4 100644 --- a/src/mal/CBCore.cpp +++ b/src/mal/CBCore.cpp @@ -738,8 +738,6 @@ std::vector blockify(const malValuePtr &arg) { if (arg == mal::nilValue()) { // Wrap none into const CBVar var{}; - var.valueType = None; - var.payload.chainState = Continue; WRAP_TO_CONST(var); } else if (const malString *v = DYNAMIC_CAST(malString, arg)) { CBVar strVar{}; @@ -789,8 +787,6 @@ malCBVarPtr varify(malCBlock *mblk, const malValuePtr &arg) { // Returns clones in order to proper cleanup (nested) allocations if (arg == mal::nilValue()) { CBVar var{}; - var.valueType = None; - var.payload.chainState = Continue; return malCBVarPtr(new malCBVar(var)); } else if (malString *v = DYNAMIC_CAST(malString, arg)) { auto &s = v->ref(); @@ -1519,7 +1515,7 @@ EXPORTED __cdecl CBVar cbLispEval(void *env, const char *str) { (*penv)->set(std::to_string(sh), malValuePtr(mvar.ptr())); return mvar->value(); } catch (...) { - return Empty; + return chainblocks::Var::Empty; } } }; diff --git a/src/tests/flows.clj b/src/tests/flows.clj index 2fb9b7d2db5..749cf26b887 100644 --- a/src/tests/flows.clj +++ b/src/tests/flows.clj @@ -43,6 +43,13 @@ (Log "res") )) +(def logicChain + (Chain + "dologic" + (IsMore 10) + (Or) + (IsLess 0))) + ;; ;; Broken for now indeed, until we implement jumps ;; ;; (def recursiveAnd @@ -102,6 +109,49 @@ 0 (Detach Loop) (WaitChain Loop) + (Assert.Is 5 true) + (Log) + + ;; test logic + ;; ensure a sub inline chain + ;; using Return mechanics + ;; is handled by (If) + -10 + (If (Do logicChain) + (--> true) + (--> false)) + (Assert.Is true false) + + -10 + (If (Do logicChain) + (--> true) + (--> false)) + (Assert.IsNot false false) + + 11 + (If (Do logicChain) + (--> true) + (--> false)) + (Assert.Is true false) + + 11 + (If (Do logicChain) + (--> true) + (--> false)) + (Assert.IsNot false false) + + 0 + (If (Do logicChain) + (--> true) + (--> false)) + (Assert.Is false false) + + 0 + (If (Do logicChain) + (--> true) + (--> false)) + (Assert.IsNot true false) + (Log "Done"))) (run Root 0.1) diff --git a/src/tests/general.clj b/src/tests/general.clj index e55d3c663c6..6b813576a2f 100644 --- a/src/tests/general.clj +++ b/src/tests/general.clj @@ -13,36 +13,42 @@ (--> (Is true)) (--> (Msg "Cond was true!!") false) (--> (Is false)) (--> (Msg "Cond was false!") true)] :Passthrough false) (Assert.Is false true) + (Log) true (Cond [ (--> (Is false) (Or) (Is true)) (--> (Msg "Cond was true!!") false) (--> (Is false)) (--> (Msg "Cond was false!") true)] :Passthrough false) (Assert.Is false true) + (Log) true (Cond [ (--> (Is false) (And) (Is true)) (--> (Assert.Is nil true)) (--> (Is true)) (--> (Msg "Cond was false!") true)] :Passthrough false) (Assert.Is true true) + (Log) false (Cond [ (--> (Is true)) (--> (Msg "Cond was true!!") false) (--> (Is false)) (--> (Msg "Cond was false!") true)] :Passthrough false) (Assert.Is true true) + (Log) false (Cond [ (--> (Is true)) (--> (Msg "Cond was true!!") false) (--> (Is false) (And) (IsNot true)) (--> (Msg "Cond was false!") true)] :Passthrough false) (Assert.Is true true) + (Log) false (Cond [ (--> (Is false) (And) (IsNot false)) (--> (Assert.Is nil true)) (--> (Is false)) (--> (Msg "Cond was true!!") false)] :Passthrough false) (Assert.Is false true) + (Log) 10 (Cond [ @@ -50,6 +56,7 @@ (--> (Is 10)) (--> (Msg "Cond was false!") true) ] :Threading true :Passthrough false) (Assert.Is true true) + (Log) 10 (Cond [ @@ -57,6 +64,7 @@ (--> (Is 10)) (--> (Msg "Cond was false!") true) ] :Threading false :Passthrough false) (Assert.Is false true) + (Log) "Hello" (Assert.Is "Hello" true) @@ -293,33 +301,6 @@ (Log) ) 6) - (Repeat (--> - (Get "index" :Default 0) - (Math.Add 1) - (Set "index") - ) 6) - (Get "index") - (Assert.Is 6 true) - - (Repeat (--> - (Get "index2" :Default 0) - (Math.Add 1) - (Set "index2") - (Cond [(--> (Is 6)) (--> (Return))]) - ) :Forever true) - (Get "index2") - (Assert.Is 6 true) - - (Repeat (--> - 10 (Set "tableInList" "x") - 20 (Set "tableInList" "y") - 30 (Set "tableInList" "z") - (Get "tableInList") - (Push "newListInRepeat" :Clear false) - ) 5) - (Get "newListInRepeat") (Log) - (Count "newListInRepeat") (Assert.Is 5 true) - 2 (Push "unsortedList") 4 (Push "unsortedList") 1 (Push "unsortedList") @@ -461,12 +442,9 @@ (Assert.Is 2 true) 10 (Set "repeatsn") - (Repeat (--> - (Get "repeatsCount" :Default 0) - (Math.Add 1) - (Set "repeatsCount") - ) (# "repeatsn")) - (Get "repeatsCount") + 0 >= .iterCount + (Repeat (--> .iterCount (Math.Add 1) > .iterCount) :Times (# "repeatsn")) + .iterCount (Assert.Is 10 true) 0 (Math.Add 10) (Assert.Is 10 true) @@ -803,14 +781,13 @@ (Chain "LoadBinary" (ReadFile "testChain.bin") - (ExpectChain) - (Set .loadedChain) - (Log) - (ChainRunner .loadedChain) - (Get .global1 :Default "nope") + (ExpectChain) >= .loadedChain (Log) + (ChainRunner .loadedChain :Mode RunChainMode.Detached) + (WaitChain .loadedChain) + (Assert.Is "global1" true) + (Get .global1 :Global true :Default "nope") (Assert.Is "global1" true) - (Log) - )) + (Log))) (if (run Root 0.001 100) nil (throw "Root tick failed")) (schedule Root testChain) diff --git a/src/tests/genetic.clj b/src/tests/genetic.clj index 5d422e2030a..e2945562bad 100644 --- a/src/tests/genetic.clj +++ b/src/tests/genetic.clj @@ -13,6 +13,7 @@ Root (Chain "test" + (Sequence .best :Types [Type.Float Type.Chain]) (Repeat (--> (Evolve @@ -26,10 +27,9 @@ fitness :Population 64 :Coroutines 8) - (Log) - (Ref "best")) + (Log) > .best) 15) - (Get "best") + .best (Take 1) (ToJson) (Log) diff --git a/src/tests/variables.clj b/src/tests/variables.clj index 6eff35e9e47..6c20cfd1e6d 100644 --- a/src/tests/variables.clj +++ b/src/tests/variables.clj @@ -171,6 +171,28 @@ (Get .global1) (Assert.Is "global1" true) + ; Seq of Floats + (Sequence .sf :Types Type.Float) + ; Seq of Floats Seqs and so on + (Sequence .sff :Types [Type.Float]) + + (Repeat + (--> + 0.1 >> .sf + .sf >> .sff) + :Times 3) + + .sf (Assert.Is [0.1 0.1 0.1] true) (Log) + .sff (Assert.Is [[0.1] [0.1 0.1] [0.1 0.1 0.1]] true) (Log) + + (Sequence .smany :Types [[Type.Float] Type.Bool]) + + true >> .smany + .sff >> .smany + ;; this is how push works... it will add Int type to the seq OK types basically + 10 >> .smany + .smany (Assert.Is [true, [[0.1], [0.1, 0.1], [0.1, 0.1, 0.1]], 10] true) (Log) + (Msg "Done!") ))