Passing asyncronous events to Tcl 
Author Message
 Passing asyncronous events to Tcl

Hello out there,
I have an extension for Tcl (written in C) which controls some
hardware. Now, when something of interest happens on the hardware
side, my extension gets notified immediately.
Problem / question: How do I pass this information to the Tcl side? I
cannot just call a function inside Tcl, can I? After all, I have no
idea what state Tcl is in, when this event happens.

Is there any "notification function" (or some such) which can be
called at arbitrary moments? Or any other means to accomplich what I
want to do?
This is on Windows, btw.

Any ideas will be greatly appreciated.
Helmut Giese



Fri, 24 Nov 2006 23:46:25 GMT  
 Passing asyncronous events to Tcl
For this you have to usr the C TCL API. Check the Tcl_AsyncCreate and
Tcl_AsyncMark

Basically you register a script using Tcl_AsyncCreate which going to be
called safely when you call the Tcl_AsyncMark command.

So in your hardware event handler you enqueue somewhere what you want to do
and call Tcl_AsyncMark

You then will be called in your registered script where you dequeue your
events and evaluate safely your event processing scripts.

IMPORTANT NOTE:

You need to save the state of the interpreter before calling the evaluation
event processing scripts.
For this do:

// Save the current state of the interpreter
        Tcl_Obj *resobj = Tcl_DuplicateObj(Tcl_GetObjResult(interp_));
        Tcl_Obj *ecodeobj = Tcl_DuplicateObj(Tcl_GetVar2Ex(interp_,
"errorCode", NULL, TCL_GLOBAL_ONLY));
        Tcl_Obj *einfoobj = Tcl_DuplicateObj(Tcl_GetVar2Ex(interp_,
"errorInfo", NULL, TCL_GLOBAL_ONLY));

// Do your processing here...

 // Restore the intepreter status
        Tcl_SetObjResult(interp_, resobj);
        Tcl_SetVar2Ex(interp_, "errorCode", NULL, ecodeobj,
TCL_GLOBAL_ONLY);
        Tcl_SetVar2Ex(interp_, "errorInfo", NULL, einfoobj,
TCL_GLOBAL_ONLY);

I am not 100% confident is really sure as sometime I still exprience strange
crashes I can't explain.

If someone can help here, I'll be happy. :-)

B.


Quote:
> Hello out there,
> I have an extension for Tcl (written in C) which controls some
> hardware. Now, when something of interest happens on the hardware
> side, my extension gets notified immediately.
> Problem / question: How do I pass this information to the Tcl side? I
> cannot just call a function inside Tcl, can I? After all, I have no
> idea what state Tcl is in, when this event happens.

> Is there any "notification function" (or some such) which can be
> called at arbitrary moments? Or any other means to accomplich what I
> want to do?
> This is on Windows, btw.

> Any ideas will be greatly appreciated.
> Helmut Giese



Sat, 25 Nov 2006 11:02:05 GMT  
 Passing asyncronous events to Tcl

Quote:

>I am not 100% confident is really sure as sometime I still exprience strange
>crashes I can't explain.

From what I've found, it's safer to call Tcl_QueueEvent from the
AsyncProc, then eval the script from the context of an EventProc.

IMO, it IS NOT SAFE the call Tcl_Eval from an AsyncProc even if you save
everything as the point where the yeild takes place is too much in the
middle of things.

Channel drivers do this a bit different.
--

[species: human; planet: earth,milkyway(western spiral arm),alpha sector]



Sat, 25 Nov 2006 11:29:03 GMT  
 Passing asyncronous events to Tcl
Thank you David,

This may explain the crashes I experience and still haven't find the reason.

I never played with Tcl_QueueEvent, do you have a sample code of how to use
this method ?

Thank you,

B.


Quote:

> >I am not 100% confident is really sure as sometime I still exprience
strange
> >crashes I can't explain.

> From what I've found, it's safer to call Tcl_QueueEvent from the
> AsyncProc, then eval the script from the context of an EventProc.

> IMO, it IS NOT SAFE the call Tcl_Eval from an AsyncProc even if you save
> everything as the point where the yeild takes place is too much in the
> middle of things.

> Channel drivers do this a bit different.
> --

> [species: human; planet: earth,milkyway(western spiral arm),alpha sector]



Sat, 25 Nov 2006 12:33:05 GMT  
 Passing asyncronous events to Tcl

Quote:

>Thank you David,

>This may explain the crashes I experience and still haven't find the reason.

>I never played with Tcl_QueueEvent, do you have a sample code of how to use
>this method ?

// ----------------------------------------------------------------------
// This class manages the Itcl interface.
// ----------------------------------------------------------------------
class HotkeyItclClassExtension
        : protected Itcl::IAdaptor<HotkeyItclClassExtension>
{
    // This structure is what gets queued into Tcl's event loop
    struct HOTKEYEVENT {
        Tcl_Event header;
        Tcl_Obj *script;
        HotkeyItclClassExtension *ext;
    };

    Tcl_AsyncHandler Async;         // Interp specific async handler.
    CMclQueue<Tcl_Obj *> workQ;       // The work queue for jobs.

public:
    HotkeyItclClassExtension(Tcl_Interp *interp)
        : Itcl::IAdaptor<HotkeyItclClassExtension>(interp)
    {
        NewItclCmd("winutils-hotkey-construct", HotKeyConstructCmd);
        NewItclCmd("winutils-hotkey-destruct", HotKeyDestructCmd);
->   Async = Tcl_AsyncCreate(FlushHotkeys, this);
    }

private:
    // ------------------------------------------------------------------
    // Our Tcl_AsyncProc is calling back into us.
    // ------------------------------------------------------------------
    void QueueAllPendingHotkeys ()
    {
        Tcl_Obj *script;
        HOTKEYEVENT *ev;

        // WARNING: Only Tcl allocation routines are allowed in here.

        // Some running HotKeyListener instance called Tcl_AsyncMark.
        // There must be at least one entry in the WorkQueue, but pull
        // them all off while we're here.  Don't let a good context go to
        // waste.
        //
        while (workQ.Get(script, 0)) {

            ev = reinterpret_cast<HOTKEYEVENT *>(
                    ckalloc(sizeof(HOTKEYEVENT)));
            ev->header.proc = EvalCallback;
            ev->ext    = this;           // Used so EvalCallback knows which
                                    // instance.
            ev->script = script;    // you came from a distant thread

            // Queue it into Tcl's event loop.
->       Tcl_QueueEvent(reinterpret_cast<Tcl_Event *>(ev),
                    TCL_QUEUE_TAIL);
        }
    }
    // ------------------------------------------------------------------
    // Our Tcl_EventProc is calling back into us from the event loop.
    // ------------------------------------------------------------------
    void EvalOneHotkey (Tcl_Obj *script)
    {
        CallFrame frame;
        Proc proc;
        Tcl_Namespace *nsPtr;
        int result;

        // let's have some fun and run it like a proc AND within a
        // namespace.
        nsPtr = Tcl_FindNamespace(interp, "::winutils", 0L, 0);
        Tcl_PushCallFrame(interp, (Tcl_CallFrame *)&frame, nsPtr, 1);

        proc.numCompiledLocals = 0;
        proc.firstLocalPtr = 0;
        proc.lastLocalPtr = 0;
        frame.procPtr = &proc;

->   result = Tcl_EvalObj(interp, script);
        Tcl_PopCallFrame(interp);

        if (result == TCL_ERROR) {
            Tcl_Obj *buff;
            int len;
            const char *str;
            buff = Tcl_NewStringObj("\nError in hotkey script \"", -1);
            Tcl_AppendObjToObj(buff, script);
            Tcl_AppendStringsToObj(buff, "\"", 0L);
            str = Tcl_GetStringFromObj(buff, &len);
            Tcl_AddObjErrorInfo(interp, str, len);
            Tcl_DecrRefCount(buff);
            Tcl_BackgroundError(interp);
        }

        Tcl_ResetResult(interp);
    }

    // ------------------------------------------------------------------
    // The Tcl_AsyncProc callback.
    // ------------------------------------------------------------------
    static int FlushHotkeys (ClientData cd, Tcl_Interp *, int code)
    {
        HotkeyItclClassExtension *ext =
                reinterpret_cast <HotkeyItclClassExtension *>(clientData);
        ext->QueueAllPendingHotkeys();
        return code;
    }

    // ------------------------------------------------------------------
    // The Tcl_EventProc callback.
    // ------------------------------------------------------------------
    static int EvalCallback (Tcl_Event *evPtr, int flags)
    {
        HOTKEYEVENT *event = reinterpret_cast<HOTKEYEVENT *>(evPtr);

        // we only handle file-type events here.
        if (!(flags & TCL_FILE_EVENTS)) return 0;
        (event->ext)->EvalOneHotkey(event->script);
        return 1;
    }

Quote:
};

FlushHotkeys is the Tcl_AsyncProc.  token is created in the constructor.
EvalCallback is the Tcl_EventProc.  The above class is incomplete, but I
didn't want to post too much.  The part that calls Tcl_AsyncMark is
located in another class, and this is the meaningfull part:

    unsigned ThreadHandlerProc(void)
    {
        MSG msg;
        BOOL ok;

        ok = RegisterHotKey(0L, 1, fsModifiers, vk);
        if (!ok) {
            status = GetLastError();
            gotReg.Set();
            return status;
        }

        // Let the main thread know we're up without an error.
        gotReg.Set();

        while (GetMessage(&msg, 0L, 0, 0) > 0) {
            switch (msg.message) {
            case WM_HOTKEY:
                // I'll be extra cautious and use SEH because I trust
                // no one ;)
                __try {
->               workQ.Put(script);
->               Tcl_AsyncMark(Async);
                }
                __except (EXCEPTION_EXECUTE_HANDLER) {
                    UnregisterHotKey(0L, 1);
                    return _exception_code();
                }
            }
        }

        UnregisterHotKey(0L, 1);

        // By convention, the exit code from the WM_QUIT is used as the
        // return value.
        //
        return msg.wParam;
    }

--

[species: human; planet: earth,milkyway(western spiral arm),alpha sector]



Sat, 25 Nov 2006 15:16:57 GMT  
 Passing asyncronous events to Tcl
Thanks Bruce and David,
this gives me something to study and think about. Looks complicated -
but then, doing async stuff isn't expected to be easy.
Best regards
Helmut Giese
Quote:
>Hello out there,
>I have an extension for Tcl (written in C) which controls some
>hardware. Now, when something of interest happens on the hardware
>side, my extension gets notified immediately.
>Problem / question: How do I pass this information to the Tcl side? I
>cannot just call a function inside Tcl, can I? After all, I have no
>idea what state Tcl is in, when this event happens.

>Is there any "notification function" (or some such) which can be
>called at arbitrary moments? Or any other means to accomplich what I
>want to do?
>This is on Windows, btw.

>Any ideas will be greatly appreciated.
>Helmut Giese



Sat, 25 Nov 2006 16:40:10 GMT  
 Passing asyncronous events to Tcl

Quote:

> Thanks Bruce and David,
> this gives me something to study and think about. Looks complicated -
> but then, doing async stuff isn't expected to be easy.

Do any of the API functions perform [set] including the execution of
traces?  Or would one have to do tcl_eval( interp, "set foo bar"...?




Sat, 25 Nov 2006 17:49:08 GMT  
 Passing asyncronous events to Tcl

Quote:


>> Thanks Bruce and David,
>> this gives me something to study and think about. Looks complicated -
>> but then, doing async stuff isn't expected to be easy.

>Do any of the API functions perform [set] including the execution of
>traces?  Or would one have to do tcl_eval( interp, "set foo bar"...?

Tcl_SetVar, Tcl_SetVar2, etc..
--

[species: human; planet: earth,milkyway(western spiral arm),alpha sector]


Sat, 25 Nov 2006 17:52:46 GMT  
 Passing asyncronous events to Tcl
Thank you David,.
I'll work on this and let you know,

B.

Quote:

> >Thank you David,

> >This may explain the crashes I experience and still haven't find the
reason.

> >I never played with Tcl_QueueEvent, do you have a sample code of how to
use
> >this method ?

> // ----------------------------------------------------------------------
> // This class manages the Itcl interface.
> // ----------------------------------------------------------------------
> class HotkeyItclClassExtension
> : protected Itcl::IAdaptor<HotkeyItclClassExtension>
> {
>     // This structure is what gets queued into Tcl's event loop
>     struct HOTKEYEVENT {
> Tcl_Event header;
> Tcl_Obj *script;
> HotkeyItclClassExtension *ext;
>     };

>     Tcl_AsyncHandler Async;     // Interp specific async handler.
>     CMclQueue<Tcl_Obj *> workQ;     // The work queue for jobs.

> public:
>     HotkeyItclClassExtension(Tcl_Interp *interp)
> : Itcl::IAdaptor<HotkeyItclClassExtension>(interp)
>     {
> NewItclCmd("winutils-hotkey-construct", HotKeyConstructCmd);
> NewItclCmd("winutils-hotkey-destruct", HotKeyDestructCmd);
> -> Async = Tcl_AsyncCreate(FlushHotkeys, this);
>     }

> private:
>     // ------------------------------------------------------------------
>     // Our Tcl_AsyncProc is calling back into us.
>     // ------------------------------------------------------------------
>     void QueueAllPendingHotkeys ()
>     {
> Tcl_Obj *script;
> HOTKEYEVENT *ev;

> // WARNING: Only Tcl allocation routines are allowed in here.

> // Some running HotKeyListener instance called Tcl_AsyncMark.
>   // There must be at least one entry in the WorkQueue, but pull
> // them all off while we're here.  Don't let a good context go to
> // waste.
> //
> while (workQ.Get(script, 0)) {

>     ev = reinterpret_cast<HOTKEYEVENT *>(
>     ckalloc(sizeof(HOTKEYEVENT)));
>     ev->header.proc = EvalCallback;
>     ev->ext    = this;     // Used so EvalCallback knows which
>     // instance.
>     ev->script = script;    // you came from a distant thread

>     // Queue it into Tcl's event loop.
> ->     Tcl_QueueEvent(reinterpret_cast<Tcl_Event *>(ev),
>     TCL_QUEUE_TAIL);
> }
>     }
>     // ------------------------------------------------------------------
>     // Our Tcl_EventProc is calling back into us from the event loop.
>     // ------------------------------------------------------------------
>     void EvalOneHotkey (Tcl_Obj *script)
>     {
> CallFrame frame;
> Proc proc;
> Tcl_Namespace *nsPtr;
> int result;

> // let's have some fun and run it like a proc AND within a
> // namespace.
> nsPtr = Tcl_FindNamespace(interp, "::winutils", 0L, 0);
> Tcl_PushCallFrame(interp, (Tcl_CallFrame *)&frame, nsPtr, 1);

> proc.numCompiledLocals = 0;
> proc.firstLocalPtr = 0;
> proc.lastLocalPtr = 0;
> frame.procPtr = &proc;

> -> result = Tcl_EvalObj(interp, script);
> Tcl_PopCallFrame(interp);

> if (result == TCL_ERROR) {
>     Tcl_Obj *buff;
>     int len;
>     const char *str;
>     buff = Tcl_NewStringObj("\nError in hotkey script \"", -1);
>     Tcl_AppendObjToObj(buff, script);
>     Tcl_AppendStringsToObj(buff, "\"", 0L);
>     str = Tcl_GetStringFromObj(buff, &len);
>     Tcl_AddObjErrorInfo(interp, str, len);
>     Tcl_DecrRefCount(buff);
>     Tcl_BackgroundError(interp);
> }

> Tcl_ResetResult(interp);
>     }

>     // ------------------------------------------------------------------
>     // The Tcl_AsyncProc callback.
>     // ------------------------------------------------------------------
>     static int FlushHotkeys (ClientData cd, Tcl_Interp *, int code)
>     {
> HotkeyItclClassExtension *ext =
> reinterpret_cast <HotkeyItclClassExtension *>(clientData);
> ext->QueueAllPendingHotkeys();
> return code;
>     }

>     // ------------------------------------------------------------------
>     // The Tcl_EventProc callback.
>     // ------------------------------------------------------------------
>     static int EvalCallback (Tcl_Event *evPtr, int flags)
>     {
> HOTKEYEVENT *event = reinterpret_cast<HOTKEYEVENT *>(evPtr);

> // we only handle file-type events here.
> if (!(flags & TCL_FILE_EVENTS)) return 0;
> (event->ext)->EvalOneHotkey(event->script);
> return 1;
>     }
> };

> FlushHotkeys is the Tcl_AsyncProc.  token is created in the constructor.
> EvalCallback is the Tcl_EventProc.  The above class is incomplete, but I
> didn't want to post too much.  The part that calls Tcl_AsyncMark is
> located in another class, and this is the meaningfull part:

>     unsigned ThreadHandlerProc(void)
>     {
> MSG msg;
> BOOL ok;

> ok = RegisterHotKey(0L, 1, fsModifiers, vk);
> if (!ok) {
>     status = GetLastError();
>     gotReg.Set();
>     return status;
> }

> // Let the main thread know we're up without an error.
> gotReg.Set();

> while (GetMessage(&msg, 0L, 0, 0) > 0) {
>     switch (msg.message) {
>     case WM_HOTKEY:
> // I'll be extra cautious and use SEH because I trust
> // no one ;)
> __try {
> ->     workQ.Put(script);
> ->     Tcl_AsyncMark(Async);
> }
> __except (EXCEPTION_EXECUTE_HANDLER) {
>     UnregisterHotKey(0L, 1);
>     return _exception_code();
> }
>     }
> }

> UnregisterHotKey(0L, 1);

> // By convention, the exit code from the WM_QUIT is used as the
> // return value.
> //
> return msg.wParam;
>     }

> --

> [species: human; planet: earth,milkyway(western spiral arm),alpha sector]



Sat, 25 Nov 2006 20:05:04 GMT  
 Passing asyncronous events to Tcl
David,

After doing testing it appears that your method works better. I put your
method in mine and ran some tests, and I had no more random crash.

So you were 100% correct saying that calling an evaluation of a script in a
AsyncProc script is unsafe and the right method is to call Tcl_QueueEvent to
push your event in the interpreter loop to be treated later.

So I have to thank you for your advise and your help on this.

Best Regards,
B.


Quote:

> >Thank you David,

> >This may explain the crashes I experience and still haven't find the
reason.

> >I never played with Tcl_QueueEvent, do you have a sample code of how to
use
> >this method ?

> // ----------------------------------------------------------------------
> // This class manages the Itcl interface.
> // ----------------------------------------------------------------------
> class HotkeyItclClassExtension
> : protected Itcl::IAdaptor<HotkeyItclClassExtension>
> {
>     // This structure is what gets queued into Tcl's event loop
>     struct HOTKEYEVENT {
> Tcl_Event header;
> Tcl_Obj *script;
> HotkeyItclClassExtension *ext;
>     };

>     Tcl_AsyncHandler Async;     // Interp specific async handler.
>     CMclQueue<Tcl_Obj *> workQ;     // The work queue for jobs.

> public:
>     HotkeyItclClassExtension(Tcl_Interp *interp)
> : Itcl::IAdaptor<HotkeyItclClassExtension>(interp)
>     {
> NewItclCmd("winutils-hotkey-construct", HotKeyConstructCmd);
> NewItclCmd("winutils-hotkey-destruct", HotKeyDestructCmd);
> -> Async = Tcl_AsyncCreate(FlushHotkeys, this);
>     }

> private:
>     // ------------------------------------------------------------------
>     // Our Tcl_AsyncProc is calling back into us.
>     // ------------------------------------------------------------------
>     void QueueAllPendingHotkeys ()
>     {
> Tcl_Obj *script;
> HOTKEYEVENT *ev;

> // WARNING: Only Tcl allocation routines are allowed in here.

> // Some running HotKeyListener instance called Tcl_AsyncMark.
>   // There must be at least one entry in the WorkQueue, but pull
> // them all off while we're here.  Don't let a good context go to
> // waste.
> //
> while (workQ.Get(script, 0)) {

>     ev = reinterpret_cast<HOTKEYEVENT *>(
>     ckalloc(sizeof(HOTKEYEVENT)));
>     ev->header.proc = EvalCallback;
>     ev->ext    = this;     // Used so EvalCallback knows which
>     // instance.
>     ev->script = script;    // you came from a distant thread

>     // Queue it into Tcl's event loop.
> ->     Tcl_QueueEvent(reinterpret_cast<Tcl_Event *>(ev),
>     TCL_QUEUE_TAIL);
> }
>     }
>     // ------------------------------------------------------------------
>     // Our Tcl_EventProc is calling back into us from the event loop.
>     // ------------------------------------------------------------------
>     void EvalOneHotkey (Tcl_Obj *script)
>     {
> CallFrame frame;
> Proc proc;
> Tcl_Namespace *nsPtr;
> int result;

> // let's have some fun and run it like a proc AND within a
> // namespace.
> nsPtr = Tcl_FindNamespace(interp, "::winutils", 0L, 0);
> Tcl_PushCallFrame(interp, (Tcl_CallFrame *)&frame, nsPtr, 1);

> proc.numCompiledLocals = 0;
> proc.firstLocalPtr = 0;
> proc.lastLocalPtr = 0;
> frame.procPtr = &proc;

> -> result = Tcl_EvalObj(interp, script);
> Tcl_PopCallFrame(interp);

> if (result == TCL_ERROR) {
>     Tcl_Obj *buff;
>     int len;
>     const char *str;
>     buff = Tcl_NewStringObj("\nError in hotkey script \"", -1);
>     Tcl_AppendObjToObj(buff, script);
>     Tcl_AppendStringsToObj(buff, "\"", 0L);
>     str = Tcl_GetStringFromObj(buff, &len);
>     Tcl_AddObjErrorInfo(interp, str, len);
>     Tcl_DecrRefCount(buff);
>     Tcl_BackgroundError(interp);
> }

> Tcl_ResetResult(interp);
>     }

>     // ------------------------------------------------------------------
>     // The Tcl_AsyncProc callback.
>     // ------------------------------------------------------------------
>     static int FlushHotkeys (ClientData cd, Tcl_Interp *, int code)
>     {
> HotkeyItclClassExtension *ext =
> reinterpret_cast <HotkeyItclClassExtension *>(clientData);
> ext->QueueAllPendingHotkeys();
> return code;
>     }

>     // ------------------------------------------------------------------
>     // The Tcl_EventProc callback.
>     // ------------------------------------------------------------------
>     static int EvalCallback (Tcl_Event *evPtr, int flags)
>     {
> HOTKEYEVENT *event = reinterpret_cast<HOTKEYEVENT *>(evPtr);

> // we only handle file-type events here.
> if (!(flags & TCL_FILE_EVENTS)) return 0;
> (event->ext)->EvalOneHotkey(event->script);
> return 1;
>     }
> };

> FlushHotkeys is the Tcl_AsyncProc.  token is created in the constructor.
> EvalCallback is the Tcl_EventProc.  The above class is incomplete, but I
> didn't want to post too much.  The part that calls Tcl_AsyncMark is
> located in another class, and this is the meaningfull part:

>     unsigned ThreadHandlerProc(void)
>     {
> MSG msg;
> BOOL ok;

> ok = RegisterHotKey(0L, 1, fsModifiers, vk);
> if (!ok) {
>     status = GetLastError();
>     gotReg.Set();
>     return status;
> }

> // Let the main thread know we're up without an error.
> gotReg.Set();

> while (GetMessage(&msg, 0L, 0, 0) > 0) {
>     switch (msg.message) {
>     case WM_HOTKEY:
> // I'll be extra cautious and use SEH because I trust
> // no one ;)
> __try {
> ->     workQ.Put(script);
> ->     Tcl_AsyncMark(Async);
> }
> __except (EXCEPTION_EXECUTE_HANDLER) {
>     UnregisterHotKey(0L, 1);
>     return _exception_code();
> }
>     }
> }

> UnregisterHotKey(0L, 1);

> // By convention, the exit code from the WM_QUIT is used as the
> // return value.
> //
> return msg.wParam;
>     }

> --

> [species: human; planet: earth,milkyway(western spiral arm),alpha sector]



Tue, 28 Nov 2006 21:48:04 GMT  
 
 [ 10 post ] 

 Relevant Pages 

1. Asyncronous Tcl? (~ built-in threads)

2. event bindings which pass the event himself first..?

3. Tcl Event Loop vs TK Event Loop issues with Asynchronous Events

4. Merging Tcl's event loop with some other event loop

5. Creating a new event source in the TCL event loop

6. Merging Tcl's event loop with other event loops (notifier)

7. Passing DLL events to parent frame?

8. Passing DLL events to parent frame

9. Pass data between subVIS using Event structures.

10. RexxArpLib Brain-deaditude:How to launch Asyncronous function?

11. GUI/event loop programming is Continuation Passing - paper

12. Does Ruby support Asyncronous Sockets on Win32

 

 
Powered by phpBB® Forum Software