• sample extension: how to beam created proc tolkens to unloadprocedure

    From Harald Oehlmann@wortkarg3@yahoo.com to comp.lang.tcl on Tue Dec 10 10:35:46 2024
    From Newsgroup: comp.lang.tcl

    Hi Wizards,

    I am soooo desperate with the sample extension.

    Standard functionality like the build-info and the dll unload feels so complicated. How can we put all this burden to the poor extension
    developpers?

    Ok, enough moaning !

    See here:
    https://core.tcl-lang.org/sampleextension/info/2c5e0e025efd0b9f

    The two commands sha1 and ::sha1::build-info are created in the init procedure.
    They have to be deleted in the unload procedure.
    How do I beam the two "tolkens" to the unload procedure ?

    struct CmdClientData {
    Tcl_Command sha1CmdTolken;
    };

    Sample_Init:
    struct CmdClientData *cmdClientDataPtr;
    cmdClientDataPtr = ckalloc(sizeof(struct Sha1ClientData)); cmdClientDataPtr->sha1CmdTolken = Tcl_CreateObjCommand(
    interp, "sha1", (Tcl_ObjCmdProc *)Sha1_Cmd,
    sha1ClientDataPtr, Sha1_CmdDeleteProc);

    Sample_Unload:
    ??? How to get cmdClientDataPtr access here ??? Tcl_DeleteCommandFromToken(interp, cmdClientDataPtr->sha1CmdTolken); ckfree(cmdClientDataPtr);

    ---

    And of cause, the git magic is not working for build-info - it does not
    build any more without git - info how this may work is nowhere...

    But that is the next step.

    Thanks for any idea!
    Harald
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Emiliano@emil.g@example.invalid to comp.lang.tcl on Tue Dec 10 12:13:05 2024
    From Newsgroup: comp.lang.tcl

    On Tue, 10 Dec 2024 10:35:46 +0100
    Harald Oehlmann <wortkarg3@yahoo.com> wrote:

    Hi Wizards,

    I am soooo desperate with the sample extension.

    Standard functionality like the build-info and the dll unload feels so complicated. How can we put all this burden to the poor extension developpers?

    Ok, enough moaning !

    See here:
    https://core.tcl-lang.org/sampleextension/info/2c5e0e025efd0b9f

    The two commands sha1 and ::sha1::build-info are created in the init procedure.
    They have to be deleted in the unload procedure.
    How do I beam the two "tolkens" to the unload procedure ?

    Sample_Init:
    struct CmdClientData *cmdClientDataPtr;
    cmdClientDataPtr = ckalloc(sizeof(struct Sha1ClientData));
    -------------------------------------------^
    Shouldn't this be "struct CmdClientDataPtr"?
    cmdClientDataPtr->sha1CmdTolken = Tcl_CreateObjCommand(
    interp, "sha1", (Tcl_ObjCmdProc *)Sha1_Cmd,
    sha1ClientDataPtr, Sha1_CmdDeleteProc);
    ---------------^
    Shouldn't this be "cmdClientDataPtr"?

    Sample_Unload:
    ??? How to get cmdClientDataPtr access here ??? Tcl_DeleteCommandFromToken(interp, cmdClientDataPtr->sha1CmdTolken); ckfree(cmdClientDataPtr);

    You get (at least!) three "levels" for storing cliendata to Tcl:

    * Per command, using the clientdata argument of Tcl_CreateObjCommand().
    * Per interp, using Tcl_{Set|Get}AssocData().
    * Per thread, using Tcl_GetThreadData().

    In this case, Tcl_GetAssocData is to be used, since you want to wipe the command out from the current interpreter, leaving other interps alone.
    A simple example (untested !!!)

    #define MY_PKG_KEY "My great package key"
    Tcl_InterpDeleteProc pkgInterpDeleted; /* called when the interp is deleted */ struct CmdClientData {
    Tcl_Command sha1CmdToken;
    };

    Sample_Init:
    struct CmdClientData *cmdClientDataPtr;
    cmdClientDataPtr = ckalloc(sizeof(struct CmdClientData));
    Tcl_SetAssocData(interp, MY_PKG_KEY, pkgInterpDeleted, cmdClientDataPtr);
    cmdClientDataPtr->sha1CmdToken = Tcl_CreateObjCommand(
    interp, "sha1", (Tcl_ObjCmdProc *)Sha1_Cmd,
    cmdClientDataPtr, Sha1_CmdDeleteProc);

    Sample_Unload:
    struct CmdClientData *cmdClientDataPtr = (struct CmdClientData *)
    Tcl_GetAssocData(interp, MY_PKG_KEY, NULL);
    /* check whether the pointer is not NULL */
    Tcl_DeleteCommandFromToken(interp, cmdClientDataPtr->sha1CmdToken);
    ckfree(cmdClientDataPtr);
    Tcl_DeleteAssocData(interp, MY_PKG_KEY);

    Of course you have to juggle the interaction of Sample_Unload, Sha1CmdDeleteProc and pkgInterpDeleted, since they can be called when:

    * [sha1] cmd is deleted (Sha1_CmdDeleteProc is called).
    * interp is destroyed (pkgInterpDeleted is called).
    * [unload] is called on the external library (Sample_Unload is called).

    Hope this helps.

    And of cause, the git magic is not working for build-info - it does not build any more without git - info how this may work is nowhere...

    But that is the next step.

    No idea here, sorry.

    Thanks for any idea!
    Harald
    --
    Emiliano
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Harald Oehlmann@wortkarg3@yahoo.com to comp.lang.tcl on Tue Dec 10 16:41:53 2024
    From Newsgroup: comp.lang.tcl

    Am 10.12.2024 um 16:13 schrieb Emiliano:
    On Tue, 10 Dec 2024 10:35:46 +0100
    Harald Oehlmann <wortkarg3@yahoo.com> wrote:

    Hi Wizards,

    I am soooo desperate with the sample extension.

    Standard functionality like the build-info and the dll unload feels so
    complicated. How can we put all this burden to the poor extension
    developpers?

    Ok, enough moaning !

    See here:
    https://core.tcl-lang.org/sampleextension/info/2c5e0e025efd0b9f

    The two commands sha1 and ::sha1::build-info are created in the init
    procedure.
    They have to be deleted in the unload procedure.
    How do I beam the two "tolkens" to the unload procedure ?

    Sample_Init:
    struct CmdClientData *cmdClientDataPtr;
    cmdClientDataPtr = ckalloc(sizeof(struct Sha1ClientData));
    -------------------------------------------^
    Shouldn't this be "struct CmdClientDataPtr"?
    cmdClientDataPtr->sha1CmdTolken = Tcl_CreateObjCommand(
    interp, "sha1", (Tcl_ObjCmdProc *)Sha1_Cmd,
    sha1ClientDataPtr, Sha1_CmdDeleteProc);
    ---------------^
    Shouldn't this be "cmdClientDataPtr"?

    Sample_Unload:
    ??? How to get cmdClientDataPtr access here ???
    Tcl_DeleteCommandFromToken(interp, cmdClientDataPtr->sha1CmdTolken);
    ckfree(cmdClientDataPtr);

    You get (at least!) three "levels" for storing cliendata to Tcl:

    * Per command, using the clientdata argument of Tcl_CreateObjCommand().
    * Per interp, using Tcl_{Set|Get}AssocData().
    * Per thread, using Tcl_GetThreadData().

    In this case, Tcl_GetAssocData is to be used, since you want to wipe the command out from the current interpreter, leaving other interps alone.
    A simple example (untested !!!)

    #define MY_PKG_KEY "My great package key"
    Tcl_InterpDeleteProc pkgInterpDeleted; /* called when the interp is deleted */
    struct CmdClientData {
    Tcl_Command sha1CmdToken;
    };

    Sample_Init:
    struct CmdClientData *cmdClientDataPtr;
    cmdClientDataPtr = ckalloc(sizeof(struct CmdClientData));
    Tcl_SetAssocData(interp, MY_PKG_KEY, pkgInterpDeleted, cmdClientDataPtr);
    cmdClientDataPtr->sha1CmdToken = Tcl_CreateObjCommand(
    interp, "sha1", (Tcl_ObjCmdProc *)Sha1_Cmd,
    cmdClientDataPtr, Sha1_CmdDeleteProc);

    Sample_Unload:
    struct CmdClientData *cmdClientDataPtr = (struct CmdClientData *)
    Tcl_GetAssocData(interp, MY_PKG_KEY, NULL);
    /* check whether the pointer is not NULL */
    Tcl_DeleteCommandFromToken(interp, cmdClientDataPtr->sha1CmdToken);
    ckfree(cmdClientDataPtr);
    Tcl_DeleteAssocData(interp, MY_PKG_KEY);

    Of course you have to juggle the interaction of Sample_Unload, Sha1CmdDeleteProc and pkgInterpDeleted, since they can be called when:

    * [sha1] cmd is deleted (Sha1_CmdDeleteProc is called).
    * interp is destroyed (pkgInterpDeleted is called).
    * [unload] is called on the external library (Sample_Unload is called).

    Hope this helps.

    Hi Emiliano,

    yes, thanks, it helps a lot. It is worse than expected...

    So, it is also a good idea, to handle the command clientData via the
    command delete proc. This is hopefully called in all cases and is
    disjunct to the other cases.

    So, we basically have a cleanup-routine, which may be called:
    - on interpreter destruction
    - on dll unload

    So, the dll is not automatically unloaded on interpreter destruction.

    If the commands were destroyed by other means (rename sha1 ""), I
    suppose, the Tcl_DeleteCommandFromToken will fail but nothing more.

    Thanks for all,
    Harald

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Harald Oehlmann@wortkarg3@yahoo.com to comp.lang.tcl on Tue Dec 10 17:11:08 2024
    From Newsgroup: comp.lang.tcl

    Am 10.12.2024 um 16:13 schrieb Emiliano:
    On Tue, 10 Dec 2024 10:35:46 +0100
    Sample_Init:
    struct CmdClientData *cmdClientDataPtr;
    cmdClientDataPtr = ckalloc(sizeof(struct Sha1ClientData));
    -------------------------------------------^
    Shouldn't this be "struct CmdClientDataPtr"?

    Yes, good catch

    cmdClientDataPtr->sha1CmdTolken = Tcl_CreateObjCommand(
    interp, "sha1", (Tcl_ObjCmdProc *)Sha1_Cmd,
    sha1ClientDataPtr, Sha1_CmdDeleteProc);
    ---------------^
    Shouldn't this be "cmdClientDataPtr"?

    Well, I use a client data for the command "sha1ClientDataPtr" with the
    command state, which is deleted by the command delete proc "Sha1_CmdDeleteProc".

    In addition, there is assoc data of the dll "cmdClientDataPtr" to keep
    the command tokens for removal within the unload procedure.

    Is this design sensible ?

    THanks for all,
    Harald
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Emiliano@emil.g@example.invalid to comp.lang.tcl on Tue Dec 10 13:37:17 2024
    From Newsgroup: comp.lang.tcl

    On Tue, 10 Dec 2024 17:11:08 +0100
    Harald Oehlmann <wortkarg3@yahoo.com> wrote:

    Am 10.12.2024 um 16:13 schrieb Emiliano:
    On Tue, 10 Dec 2024 10:35:46 +0100
    Sample_Init:
    struct CmdClientData *cmdClientDataPtr;
    cmdClientDataPtr = ckalloc(sizeof(struct Sha1ClientData));
    -------------------------------------------^
    Shouldn't this be "struct CmdClientDataPtr"?

    Yes, good catch

    cmdClientDataPtr->sha1CmdTolken = Tcl_CreateObjCommand(
    interp, "sha1", (Tcl_ObjCmdProc *)Sha1_Cmd,
    sha1ClientDataPtr, Sha1_CmdDeleteProc);
    ---------------^
    Shouldn't this be "cmdClientDataPtr"?

    Well, I use a client data for the command "sha1ClientDataPtr" with the command state, which is deleted by the command delete proc "Sha1_CmdDeleteProc".

    In addition, there is assoc data of the dll "cmdClientDataPtr" to keep
    the command tokens for removal within the unload procedure.

    Is this design sensible ?

    Can't say without seeing the real code, but ...

    * If you delete the command [sha1], either using [rename] or by deleting
    the current interpreter or namespace, you might want to unload the
    library as well, and free all allocated resources;
    * If you [unload] the library, you might want to also delete the [sha1]
    command, and free all allocated resources.
    * If you are using Tcl_{Get|Set}AssocData, you have to check the right
    time to use ckfree if both cleanup functions depends on clientdata
    to be valid. In this case, Tcl_Preserve/Tcl_Release and
    Tcl_EventuallyFree might be useful. Or maybe adding a flag in the
    clientdata structure to mark "This cleanup function was already been called".


    THanks for all,
    Harald

    Regards
    --
    Emiliano
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Harald Oehlmann@wortkarg3@yahoo.com to comp.lang.tcl on Tue Dec 10 18:09:43 2024
    From Newsgroup: comp.lang.tcl

    Am 10.12.2024 um 17:37 schrieb Emiliano:
    On Tue, 10 Dec 2024 17:11:08 +0100
    Harald Oehlmann <wortkarg3@yahoo.com> wrote:

    Am 10.12.2024 um 16:13 schrieb Emiliano:
    On Tue, 10 Dec 2024 10:35:46 +0100
    Sample_Init:
    struct CmdClientData *cmdClientDataPtr;
    cmdClientDataPtr = ckalloc(sizeof(struct Sha1ClientData));
    -------------------------------------------^
    Shouldn't this be "struct CmdClientDataPtr"?

    Yes, good catch

    cmdClientDataPtr->sha1CmdTolken = Tcl_CreateObjCommand(
    interp, "sha1", (Tcl_ObjCmdProc *)Sha1_Cmd,
    sha1ClientDataPtr, Sha1_CmdDeleteProc);
    ---------------^
    Shouldn't this be "cmdClientDataPtr"?

    Well, I use a client data for the command "sha1ClientDataPtr" with the
    command state, which is deleted by the command delete proc
    "Sha1_CmdDeleteProc".

    In addition, there is assoc data of the dll "cmdClientDataPtr" to keep
    the command tokens for removal within the unload procedure.

    Is this design sensible ?

    Can't say without seeing the real code, but ...

    * If you delete the command [sha1], either using [rename] or by deleting
    the current interpreter or namespace, you might want to unload the
    library as well, and free all allocated resources;
    * If you [unload] the library, you might want to also delete the [sha1]
    command, and free all allocated resources.
    * If you are using Tcl_{Get|Set}AssocData, you have to check the right
    time to use ckfree if both cleanup functions depends on clientdata
    to be valid. In this case, Tcl_Preserve/Tcl_Release and
    Tcl_EventuallyFree might be useful. Or maybe adding a flag in the
    clientdata structure to mark "This cleanup function was already been called".


    THanks for all,
    Harald

    Regards

    Hi Emiliano,
    thanks, great !

    Here is the current proposal:

    https://core.tcl-lang.org/sampleextension/file?name=generic/tclsample.c&ci=tip

    I thought, that the command client data is really tied to the command.
    And the clean-up is called on command deletion. That is easier, as there
    is the clientData pointer at each command interaction (Call, Deletion).

    I think, assoc data has only be used for commands, if the data should be available outside of those interactions, like event processing.
    Here, this is not the case.
    But it may be added as a comment.

    About the DLL, the unload or interpreter destruction should care.

    To unload the DLL by "rename sha1 {}" would be an incredible feature I
    don't want to think of. Users may use "unload <PathToDll>" to do so.

    Thanks for all,
    Harald
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Harald Oehlmann@wortkarg3@yahoo.com to comp.lang.tcl on Wed Dec 11 11:27:14 2024
    From Newsgroup: comp.lang.tcl

    Am 10.12.2024 um 16:13 schrieb Emiliano:
    On Tue, 10 Dec 2024 10:35:46 +0100
    Harald Oehlmann <wortkarg3@yahoo.com> wrote:
    You get (at least!) three "levels" for storing cliendata to Tcl:

    * Per command, using the clientdata argument of Tcl_CreateObjCommand().
    * Per interp, using Tcl_{Set|Get}AssocData().
    * Per thread, using Tcl_GetThreadData().

    I found another one. The command
    Tcl_CreateNamespace(interp, name, clientData, deleteProc)
    also has a clientData and deleteProc argument.

    When the command of the sample application is put into a namespace (IMHO
    a good practice), we can use the namespace clientData to store the
    command tolkens.

    This may avoid the associated data.

    Thanks for all,
    Harald
    --- Synchronet 3.20a-Linux NewsLink 1.114