Tcl Source Code

View Ticket
Login
Ticket UUID: 2844962
Title: Tcl_UpdateLinkedVar does not trigger tk widget updates
Type: Bug Version: obsolete: 8.6b1
Submitter: kchansen Created on: 2009-08-26 14:21:51
Subsystem: 09. Linked C Variables Assigned To: dgp
Priority: 5 Medium Severity:
Status: Closed Last Modified: 2009-09-03 15:08:21
Resolution: Invalid Closed By: kchansen
    Closed on: 2009-09-02 16:19:19
Description:
Also tested in 8.5.7, same behavior.

OS Platform & Version WinXP with SP3

Problem Behavior
  Updates to linked C variables do not trigger widget updates.

  If I update a Tcl variable that is tied to a widget, the widget is automatically updated, presumably via hooks similar to the trace mechanism.  Unfortunately, this does not seem to occur if the variables are tied to C variables.  Calling Tcl_UpdateLinkedVar() *does* update the Tcl-side, since the following proc demonstrates the updates are occurring by displaying the Tcl-side values every two seconds when called via "Update 2000":

proc Update {interval} {
  if {$::Thresh} {
    puts "***************************************************************"
    puts "Red:   $::RMax"
    puts "Green: $::GMax"
    puts "Blue:  $::BMax"
    puts "Interval $interval"
    update
    }
  after $interval "Update $interval"
  }

Even though the Tcl variables reflect the updates, the widget values DO NOT refresh until I manipulate the widget window (e.g. resize).

Below are the pertinent code snippets.  I originally tried defining R/G/BMax as longs, and using them directly, but it occurred to me that perhaps the update needed to have string variables since the widgets use "-textvariable" options.  Changing the code to the below code had the same behavior.  I also tried calling Tcl_InvalidateStringRep() in the same C routine which calls Tcl_UpdateLinkedVar() with no changes to behavior.

***tcl***
  label .v2.lrmax.rMax -textvariable ::RMax
  label .v2.lgmax.gMax -textvariable ::GMax
  label .v2.lbmax.bMax -textvariable ::BMax

***init***
  sRMax  = Tcl_Alloc(256);
  sGMax  = Tcl_Alloc(256);
  sBMax  = Tcl_Alloc(256);
  Tcl_LinkVar(theInterp, "::RMax",   (char *) &sRMax,
TCL_LINK_STRING | TCL_LINK_READ_ONLY);
  Tcl_LinkVar(theInterp, "::GMax",   (char *) &sGMax,
TCL_LINK_STRING | TCL_LINK_READ_ONLY);
  Tcl_LinkVar(theInterp, "::BMax",   (char *) &sBMax,
TCL_LINK_STRING | TCL_LINK_READ_ONLY);

***update***
    sprintf(sRMax, "%lu", RMax);
    Tcl_UpdateLinkedVar(mainInterp, "::RMax");
    sprintf(sGMax, "%lu", GMax);
    Tcl_UpdateLinkedVar(mainInterp, "::GMax");
    sprintf(sBMax, "%lu", BMax);
    Tcl_UpdateLinkedVar(mainInterp, "::BMax");
User Comments: dkf added on 2009-09-03 15:08:21:

allow_comments - 1

dkf added on 2009-09-03 15:08:17:
Added some text to HEAD documentation. It mentions Tcl_ThreadQueueEvent and Tcl_AsyncMark, which indicate the two key methods for handling this sort of thing (they're totally different strategies BTW).

kchansen added on 2009-09-02 23:19:17:
EUREKA!

<Pauses while epiphany sinks in>

I was *wondering* how one would protect other code from a misbehaving callback.  I think we definitely need to add links from the *Link* descriptions to the *Async* descriptions, with a note that says something to the effect:

"Critical Note: Modifying a linked variable asynchronously from C (e.g. in response to a signal or interrupt) requires additional measures to protect the system.  See Tcl_AsyncCreate() and Tcl_AsyncMark() for details."

I have tagged this as *CLOSED-INVALID*, but definitely want to push for the addition to the documentation.  I have left the status as "OPEN" for comment posting.

I added a call to Tcl_AsyncCreate() to my init function, and my callback now does the calcs against the C variables, then calls Tcl_AsyncMark().  The async handler calls the Tcl_UpdateLinkedVar() for each of the touched variables.  Now the values are updating beautifully!

Thanks again for the quick work on this!

I would have left this open and just changed the category but "other" is too vague, and I don't see a category for "documentation/helpfile"....

ferrieux added on 2009-09-02 14:19:13:
Well, a callback from an alien thread is *not* the way Tcl is supposed to be notified. The Async.3 manpage has the details.

kchansen added on 2009-09-02 06:03:48:
The demo is large because of all the hoops that Micro$soft created through which code must jump in order to hook live video streams.  All I *really* want to do is collect raw buffers from a streaming source at the other end of a firewire or USB 2.0+ connection.

The video hook is in a different thread, but calls back into the Tcl thread asynchronously to the routine in Callback.cpp.  Once my thread gets notified I am just counting the frames.

Perhaps a simple dual-thread system might work to demonstrate it.

I'll see if I can create a dual-thread demo where one thread waits for random intervals on a timer and then notifies the Tcl code via a callback.

Thanks for looking at it!

I'm working on porting the library to Linux and Mac, but my real job gets in the way....  :)

dgp added on 2009-09-02 00:51:28:
No, it's a much bigger and more complex demo
than I have time to dissect now.

The essense of your bug report seems simple.
Why such a big demo?

dgp added on 2009-09-02 00:47:12:
oops.  sorry, after looking, I can't
work with that demo kit.

I don't do Windows.

I'll open the sources in case they adapt
easily.

kchansen added on 2009-08-30 06:06:29:
Thanks for looking at this!

Unzip the file "CleanExample.zip" to an empty directory.  It contains four files:

VidKt.dll -- A video toolkit with Tcl/Tk hooks (display video in any frame!  :) )
   (This dll is copyright 2009, Karl C. Hansen, All rights reserved.)  It is provided to assist with debugging this issue.

Callback.cpp -- The video callback hook that serves up frames to the Tcl code.  Also renders frames into the display window.

TclUtils.cpp -- The Tcl-side of the interface, simply counts the frames.  Also handles initialization of the global variables.

vt.tcl -- builds the video control panel, designates the display window, creates a window to show the counter, starts the video stream.  Shows the Tcl console.

Clicking on "Show Updates" in the little counter window starts displaying the current value of Counter every two seconds.  Counter is changing when displayed via Tcl code, but not in the little widget.

If you resize the window displaying Counter, the value will update.

If you drag *any* window in front of the counter window then away, the value updates.  It really looks like the widget just is not redrawing even though the text variable has changed....

kchansen added on 2009-08-30 05:59:01:

File Added - 341062: CleanExample.zip

kchansen added on 2009-08-30 05:57:49:
I have created a stripped-down demo that does not depend on any external DLLs (other than my own DLL & Tcl/Tk dlls).  I'll create a zip file with the source files for the Tcl interface and the tcl script demonstrating the problem.

dgp added on 2009-08-28 04:58:15:
It might be helpful if the original reporter
would attach some working demos to
this report.  Then there could be less
guessing and more analysis.

dgp added on 2009-08-28 04:55:39:
Ah, perhaps the widgets are being updated just
fine, but their displayed appearance does not
reflect the new values yet because the event
loop has not yet processed idle handlers?  Any
chance that is the real problem here?

dgp added on 2009-08-28 04:54:02:
Are you calling Tcl_UpdateLinkedVar() within
the handler of a trace on the linked Tcl variable?

If not, I'm not seeing easily how you could have
the failure you report.  The guts of 
Tcl_UpdateLinkedVar() is simply a call
to Tcl_ObjSetVar2() which should trigger
write traces in this context just the same
as it does in any other context.

Attachments: