MkKernel PACKAGE - Storage Management …
MkKernel PACKAGE - Storage Management …
Storage management is used in libmkkernel to provide temporary storage. It is a common design pattern that libmkkernel only returns a reference to the Internal-Temporary-Storage (ITS), so the Internal-Active-Storage (IAS) is not returned to the external end user. The ITS is a storage that is only used as a return value and nothing else. The temporary in ITS refers exclusively to the current state of the storage and not to the lifespan of the storage, the ITS is only allocated once at startup and then used again and again, similar to the static storage in C.
Internal libmkkernel distinguishes three different storage sources:
The CLS and FLS have the same visibility to the end user and are explained together as FLS.
The RLS is not mentioned in this documentation section because the RLS is more internal than CLS and FLS.
The end-user uses a FLS reference like a normal local C variable but with the following restriction:
The "Dup" (duplicate) function is used to convert a temporary FLS variable into a global storage. The global storage is managed by the end user and may have to be released depending on the target programming language.
Example from server.c
→ ReadBFL overwrite previous ReadBFL
static enum MkErrorE Ot_BFL2 ( MQ_SERVICE_CALL_ARGS ) { MK_BFL tmp1 = MqReadBFL_e(mqctx); // "tmp1" is now a reference to the FLS storage of "ReadBFL" MK_BFL tmp2 = MqReadBFL_e(mqctx); // ERROR: the "tmp2" is using a SHARED reference with "tmp1" MqSend_E(mqctx, "R", "LL", tmp1, tmp2); // ERROR: "$tmp1" and "$tmp2" are the SAME values return MK_OK; error: return MqSendRETURN (mqctx); }
Example from server.c
→ ReadBFL overwrite previous ReadBFL even in an Event-Loop
static enum MkErrorE pBFL3 ( MQ_SERVICE_CALL_ARGS ) { __attribute__((unused)) MK_BFL tmp2 = MqReadBFL_e(mqctx); // ERROR: the "tmp2" is using a SHARED reference with "tmp1" return MK_OK; error: return MkErrorStack_1X(mqctx); } static enum MkErrorE Ot_BFL3 ( MQ_SERVICE_CALL_ARGS ) { MK_BFL tmp1 = MqReadBFL_e(mqctx); // "tmp1" is now a reference to the FLS storage of "ReadBFL" MqSend_E(mqctx, "C", pBFL3, "ECOL:[III]", 4, 5, 6); // ATTENTION: callback "pBFL3" using "ReadBFL" MqProcessEvent_E(mqctx,MQ_WAIT_OWN,MK_TIMEOUT_DEFAULT); // ERROR: enter event-loop, callback "pBFL3" is called MqSend_E(mqctx, "R", "L", tmp1); // ERROR: "tmp1" has now the value from "tmp2" return MK_OK; error: return MqSendRETURN (mqctx); }
Example from server.c
→ convert ReadBFL result into global storage using Dup and free later
static enum MkErrorE Ot_BFL4 ( MQ_SERVICE_CALL_ARGS ) { MK_BFL tmp1 = MqReadBFL_e(mqctx); // "tmp1" is now a reference to the FLS storage of "ReadBFL" MK_BFL glb1 = MkBufferListDup(tmp1); // OK: "glb1" is now a UNSHARED reference to the global memory MK_BFL tmp2 = MqReadBFL_e(mqctx); // "tmp2" is now a reference to the FLS storage of "ReadBFL" MqSend_E(mqctx, "R", "LL", glb1, tmp2); // OK: "glb1" (alias tmp1) and "tmp2" are separate references MkBufferListDelete(glb1); // without "garbage-collection" the global memory must be released return MK_OK; error: return MqSendRETURN (mqctx); }
*CreateTLS
style of functionsIn the C language the TLS (Thread-Local-Storage) is unique per definition and the name is used to distinguish the storage.
The Problem is to create a TLS interface useable in all Target-Programming-Language (TPL) supported by the Programming-Language-Micro-Kernel (PLMK).
The *CreateTLS
style function return a TLS that is a global storage. global mean unique per runtime and not unique per definition. The string tlsid is used to distinguish the storage on the global level.
Every
*CreateTLS
style function with the same tlsid return the same memory in the same thread.
There is always a risk that the memory used by the *CreateTLS
style of functions will also be used by another component of the software in the same thread.
*CreateTLS
style function with caution in a local (controlled) context.enum
Example from perfserver.c
→ performance test with TLS storage in a local (controlled) context
static enum MkErrorE Ot_BUST ( MQ_SERVICE_CALL_ARGS ) { MK_BUS bus = MkBufferStreamCreateTLS_1( "perfserver-BUST" ); while (MqReadItemExists(mqctx)) { MkBufferStreamWriteBUF(bus,MqReadBUF_e(mqctx)); } MkBufferStreamPosToStart(bus); MqSendSTART_E (mqctx); while (MkBufferStreamReadItemExists(bus)) { MqSendBUF_E (mqctx, MkBufferStreamReadBUF_e (bus)); } error: return MqSendRETURN (mqctx); }
Example from LibSq3LiteRpcClient.tcl
→ callback dealing the temporary TLS data
# Intro : Example from tcl-rpc-client of using a CreateTLS-like function (here for MkBufferListC) # to improve code speed and readability. # # Problem : This function is used to invoke a callback (myCb). The arguments come from the argument # list args *and* from a service call (ReadBFL). # The problem is that ReadBFL is called *twice* and the *second* call overwrites the value # of the *first* call because CreateTLS always returns *the same* MkBufferListC, just # replaced with a new set of values. # # Solution : The MkBufferListC instance returned by ReadBFL is copied into another MkBufferListC # instance returned by CreateTLS. # The "CreateTLS" instance is only created *once* and reused, *but* now we can create as # many MkBufferListC instances as we want, because "CreateTLS" distinguishes the returned # instances by the string identifier. # WITHOUT "CreateTLS" a copy would have to be created (Dup) which would then be destroyed # *after* the callback is called (Delete) proc Sq3LiteRpcClientExecV2CB {rpc myCb args} { set valL [MkBufferListC CreateTLS "Sq3LiteRpcClientExecV2CB→valL"] set colL [MkBufferListC CreateTLS "Sq3LiteRpcClientExecV2CB→colL"] $valL Copy [$rpc ReadBFL] $colL Copy [$rpc ReadBFL] $myCb {*}$args $valL $colL }