• confused about thread::wait and thread::send

    From ted brown@21:1/5 to All on Mon Aug 30 19:22:47 2021
    I'm a bit confused about just what thread::wait does. The manual says it
    should be the very last command in the script. I'm assuming that also
    applies to using it in a loop.

    I was thinking about some code in a thread that would accept some data
    to work on, and use thread::wait to wait for something to be sent. But
    it seems I can't do what I would normally think of as a standard work
    loop like so:

    set t1 [thread::create {
    while 1 {
    thread::wait
    .. wake up when something arrives ...
    process arrival
    send back result
    }
    } ]

    Also, on the thread::send there's the ?varname? last arg. Can it
    retrieve the result more than once if more than one thread::send is
    done? What if a send occurs while an earlier one is still processing?
    Does it queue them up?

    Since code is sent, rather than data, will they all trigger together
    before it even wakes up?

    Or do I have this completely wrong?

    What's the best way to do this (best = simplest in this case)?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rich@21:1/5 to ted brown on Tue Aug 31 03:23:16 2021
    ted brown <tedbrown888@gmail.com> wrote:
    I'm a bit confused about just what thread::wait does. The manual says it should be the very last command in the script. I'm assuming that also
    applies to using it in a loop.

    It enters the event loop. Useful if your script is in a tclsh that
    does not normally have an event loop running. Not technicaly needed if
    your script runs in a wish (or by package require Tk) where you always
    have an event loop running.

    I was thinking about some code in a thread that would accept some data
    to work on, and use thread::wait to wait for something to be sent. But
    it seems I can't do what I would normally think of as a standard work
    loop like so:

    set t1 [thread::create {
    while 1 {
    thread::wait
    .. wake up when something arrives ...
    process arrival
    send back result
    }
    } ]

    That looks a lot like yield for coroutines, thread send messages don't
    work that way.

    Also, on the thread::send there's the ?varname? last arg. Can it
    retrieve the result more than once if more than one thread::send is
    done? What if a send occurs while an earlier one is still processing?
    Does it queue them up?

    I believe they queue up and you can get them back in the order they
    were created.

    Since code is sent, rather than data, will they all trigger together
    before it even wakes up?

    Each 'send' is a separate "event".

    Or do I have this completely wrong?

    A bit mixed up, here and there, but not completely wrong.

    What's the best way to do this (best = simplest in this case)?

    A pattern I've used before is to treat thread::sends as 'messages' (I
    often toss them over using -async as well, in which case they become
    'fire and forget'). And then in the receiver, have it 'thread::send'
    it's result on to where it should go. I.e., something like this psudeo
    code:

    parent thread code (assume Tk is running):

    set t1 [thread::create {
    proc do-something {a b c} {
    lappend result [do-something1 $a] ;# assume do-something
    lappend result [do-something2 $b]
    lappend result [do-something3 $c]
    # assume this thread was 'informed' somehow of the parent thread ID
    # (tsv is useful for this)
    thread::send -async $parent [list the-answer-is $result]
    }
    }

    proc the-answer-is {result} {
    display-the-answer $result ;# assume 'display-the-answer' does
    # whatever necessary to show/print/output/etc. the answer
    }

    button .b -text "Do something" \
    -command [list thread::send -async $t1 do-something $z $y $x]
    # assume z y x are -textvars connected to other widgets

    Pressing the button triggers an event that runs the -command, which
    does an async thread::send into the thread, calling the proc inside the
    thread. Multiple 'sends' to the thread queue up on the thread's event
    loop and are processed in order.

    The thread, when done, then does its own async thread send back to the
    parent, to a different proc, that handles the "display" of the data in
    some way.

    This is sort of a RPC/message hybrid concept. The individual async
    thread send's are passing messages around. The messages execute
    "remote procedures".

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From ted brown@21:1/5 to Rich on Mon Aug 30 22:51:46 2021
    On 8/30/2021 8:23 PM, Rich wrote:

    Hmmm, interesting. I think I see, just a few follow-up questions.


    parent thread code (assume Tk is running):

    set t1 [thread::create {
    proc do-something {a b c} {
    lappend result [do-something1 $a] ;# assume do-something
    lappend result [do-something2 $b]
    lappend result [do-something3 $c]
    # assume this thread was 'informed' somehow of the parent thread ID
    # (tsv is useful for this)
    thread::send -async $parent [list the-answer-is $result]
    }
    }

    So, this seems similar to wish/tclsh running a script where if wish,
    reaching the end of the script file it enters the event loop, while if
    started by tclsh, it exits instead - or one uses an explicit vwait.

    So, analogously reaching the end of the thread script also either enters
    the event loop (if started with wish) or exits (if started by tclsh).

    Is that the right way to think about it?

    And so the thread::wait does what the vwait does in the regular script
    when you startup with tclsh.

    Now with regular events, if the code triggered by an event does a vwait
    itself, then the next queued event will trigger, which in our case here,
    with just the 1 proc, would re-enter do-something interrupting the
    earlier call to do-something. Is that correct?

    So to keep do-something from being interrupted, one must not do
    anything, like an update, or vwait. Or else, I guess to keep them
    sequential, one would have to use some thread locking primitives.



    proc the-answer-is {result} {
    display-the-answer $result ;# assume 'display-the-answer' does
    # whatever necessary to show/print/output/etc. the answer
    }


    So, this proc is similar to one that might be called via the script arg
    in a bind call except the "bounded" event is the thread::send back to
    the parent.


    button .b -text "Do something" \
    -command [list thread::send -async $t1 do-something $z $y $x]
    # assume z y x are -textvars connected to other widgets


    Is z, y, and x, being connected to widgets just as an example, or is
    there something special about those 3 variables?

    Also, I thought the script had to be in one arg, should that be

    -command [list thread::send -async $t1 [list do-something $z $y $x]]

    This is sort of a RPC/message hybrid concept. The individual async
    thread send's are passing messages around. The messages execute
    "remote procedures".


    Thanks so much, I think I now have a way to think about it that is not
    too different from what I'm used to - assuming my above thinking is
    correct :)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From EL@21:1/5 to ted brown on Tue Aug 31 05:17:25 2021
    ted brown <tedbrown888@gmail.com> wrote:
    I'm a bit confused about just what thread::wait does. The manual says it should be the very last command in the script. I'm assuming that also
    applies to using it in a loop.

    No, you don’t need a loop. Call thread::wait directly and you‘re set. And if you create the thread without a script, with [thread::create], then the thread waits automatically.

    But it seems that you want to do something that is not achievable with thread::wait, namely sending signals between threads and let one thread
    wait for another to finish some work etc. This is best done with thread::condition variables. It’s also where the loop comes into the game. Condition variables enable you to wait in a thread for something that
    happens in another thread, you can use them to signal there and back
    from/to the threads in your program.


    --
    EL

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From ted brown@21:1/5 to All on Mon Aug 30 22:59:00 2021
    On 8/30/2021 10:17 PM, EL wrote:
    ted brown <tedbrown888@gmail.com> wrote:
    I'm a bit confused about just what thread::wait does. The manual says it
    should be the very last command in the script. I'm assuming that also
    applies to using it in a loop.

    No, you don’t need a loop. Call thread::wait directly and you‘re set. And if you create the thread without a script, with [thread::create], then the thread waits automatically.

    But it seems that you want to do something that is not achievable with thread::wait, namely sending signals between threads and let one thread
    wait for another to finish some work etc. This is best done with thread::condition variables. It’s also where the loop comes into the game. Condition variables enable you to wait in a thread for something that
    happens in another thread, you can use them to signal there and back
    from/to the threads in your program.



    Thanks, I'll look into the condition variables once I fully understand
    what Rich wrote.

    Two things though, puts is my usual debugger, and on windows I can't get
    puts to go to a console (from inside a thread::create script) nor can I
    see any errors pop up. It seems the thread just quits silently if I mess up.

    How do you debug your threads?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From EL@21:1/5 to ted brown on Tue Aug 31 08:37:30 2021
    ted brown <tedbrown888@gmail.com> wrote:

    Two things though, puts is my usual debugger, and on windows I can't get
    puts to go to a console (from inside a thread::create script) nor can I
    see any errors pop up. It seems the thread just quits silently if I mess up.

    How do you debug your threads?


    When you use tclsh on windows from a cmd or powershell rather than wish,
    the puts goes on the cmd/ps. That’s how I do it…

    But admittedly I don’t use the Threads package a lot, I usually work with threads in C/C++ in Tcl extensions. Just stumbled over condition variables
    in the Threads package documentation recently, and found out that it’s the same concept as with C++ std::condition_variable. Not really surprising, though, but I was surprised that they also exist at the Tcl level ;)


    --
    EL

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rich@21:1/5 to ted brown on Tue Aug 31 11:52:29 2021
    ted brown <tedbrown888@gmail.com> wrote:
    On 8/30/2021 10:17 PM, EL wrote:
    ted brown <tedbrown888@gmail.com> wrote:
    I'm a bit confused about just what thread::wait does. The manual says it >>> should be the very last command in the script. I'm assuming that also
    applies to using it in a loop.

    No, you don?t need a loop. Call thread::wait directly and you?re set. And
    if you create the thread without a script, with [thread::create], then the >> thread waits automatically.

    But it seems that you want to do something that is not achievable with
    thread::wait, namely sending signals between threads and let one thread
    wait for another to finish some work etc. This is best done with
    thread::condition variables. It?s also where the loop comes into the game. >> Condition variables enable you to wait in a thread for something that
    happens in another thread, you can use them to signal there and back
    from/to the threads in your program.



    Thanks, I'll look into the condition variables once I fully understand
    what Rich wrote.

    Two things though, puts is my usual debugger, and on windows I can't get
    puts to go to a console (from inside a thread::create script) nor can I
    see any errors pop up. It seems the thread just quits silently if I mess up.

    How do you debug your threads?

    By not using Windows as the OS.... :) (yes, really).

    However, for those instances where I am forced to build some Tcl on
    that inferior OS, what I do is one of:

    1) create a "log" (or other suitable name) proc in the parent
    (original) thread
    2) create "wrapper" procs in the child threads that abstracts away a
    'thread::send -async $parent [list log ....]' that is used instead
    of 'puts' in the child threads.

    or

    1) load the tcllib 'log' package in the parent
    2) create wrapper procs in the child threads that abstract away a
    'thread::send -async $parent [??? .....]' for the various ???
    procedures of the log package I want to use from the children
    threads

    And, as the master parent thread 'can' open a console on Windows, I can
    see the 'puts' messages there. Or if I've used Tcllib's log, it allows redirecting the log output to a file.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rich@21:1/5 to ted brown on Tue Aug 31 11:45:09 2021
    ted brown <tedbrown888@gmail.com> wrote:
    On 8/30/2021 8:23 PM, Rich wrote:

    Hmmm, interesting. I think I see, just a few follow-up questions.


    parent thread code (assume Tk is running):

    set t1 [thread::create {
    proc do-something {a b c} {
    lappend result [do-something1 $a] ;# assume do-something
    lappend result [do-something2 $b]
    lappend result [do-something3 $c]
    # assume this thread was 'informed' somehow of the parent thread ID >> # (tsv is useful for this)
    thread::send -async $parent [list the-answer-is $result]
    }
    }

    So, this seems similar to wish/tclsh running a script where if wish,
    reaching the end of the script file it enters the event loop, while if started by tclsh, it exits instead - or one uses an explicit vwait.

    Yes.

    So, analogously reaching the end of the thread script also either enters
    the event loop (if started with wish) or exits (if started by tclsh).

    This has been my experience so far.

    Is that the right way to think about it?

    It seems reasonable. Others may wish to add corrections.

    And so the thread::wait does what the vwait does in the regular script
    when you startup with tclsh.

    Now with regular events, if the code triggered by an event does a vwait itself, then the next queued event will trigger, which in our case here,
    with just the 1 proc, would re-enter do-something interrupting the
    earlier call to do-something. Is that correct?

    This is very likely, so you would need to use care (as always) when
    having a proc in a thread perform an additional vwait.

    So to keep do-something from being interrupted, one must not do
    anything, like an update, or vwait. Or else, I guess to keep them
    sequential, one would have to use some thread locking primitives.

    Either would be possible, and the threads package does provide the
    usual set of sync. primitives. But with the exception of needing to
    handle code that does a 'vwait' you don't otherwise have to worry about
    these items.

    proc the-answer-is {result} {
    display-the-answer $result ;# assume 'display-the-answer' does
    # whatever necessary to show/print/output/etc. the answer
    }


    So, this proc is similar to one that might be called via the script arg
    in a bind call except the "bounded" event is the thread::send back to
    the parent.

    In a way, yes. The flow becomes event driven. Button press event
    results in "message send" event to the other thread. Later, when the
    other thread finishes, it creates another "message send" event back to
    the parent (or to some other 'next' thread).

    button .b -text "Do something" \
    -command [list thread::send -async $t1 do-something $z $y $x] >> # assume z y x are -textvars connected to other widgets


    Is z, y, and x, being connected to widgets just as an example, or is
    there something special about those 3 variables?

    Just example names to fill in the psudeocode. I.e., think of them as
    being attached to entry widgets via the -textvariable option.

    Also, I thought the script had to be in one arg, should that be

    -command [list thread::send -async $t1 [list do-something $z $y $x]]

    Yes, you are correct. I did omit another "listification" there. Well,
    I did call it "psudeocode". :)

    This is sort of a RPC/message hybrid concept. The individual async
    thread send's are passing messages around. The messages execute
    "remote procedures".


    Thanks so much, I think I now have a way to think about it that is
    not too different from what I'm used to - assuming my above thinking
    is correct :)

    You appear (to the extent I can tell via Usenet) to be on a reasonable
    path. And, as always, one of the best ways to learn is to give it a
    try.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From ted brown@21:1/5 to Rich on Tue Aug 31 13:07:21 2021
    On 8/31/2021 4:52 AM, Rich wrote:


    How do you debug your threads?

    By not using Windows as the OS.... :) (yes, really).

    I too have a love hate relationship with windows. Mostly I use it
    because I have 25 years worth of tools I've collected that still run. I
    even still use Office 97's excel for some rather old spreadsheets.

    But it goes back even further, since Dave Cutler's prior 2 OS's on the
    pdp-11 and vax were the precursor to windows NT. I can still see some of
    Dave's style in how Windows internals work. But even then it was the
    third party hardware/software that was the benefit. Pdp-11's were
    everywhere back then.

    I also have some rather nice windows development tools by a guy named
    Jan Goyvaerts who markets editpad pro, powergrep, and regexbuddy. I have
    a few linux systems in VMs with a rasp pi also, and I can easily run
    these tools on files there using samba, and also edit files directly on
    my android phones and tablets over sftp, also built into the tools.

    And on windows, with tcl/tk, there's Ashok's twapi, which I use very
    often for scripting tedious gui actions. I can even synchronize some
    parts of my script by waiting until some gui window's title changes, so
    I know the program is ready for some mouse clicks sent to it.

    But I hate win 10's forced updates so much, I block them with my
    router's access control page.


    And, as the master parent thread 'can' open a console on Windows, I can
    see the 'puts' messages there. Or if I've used Tcllib's log, it allows redirecting the log output to a file.


    Hmmm, maybe I'll just need to build something that can run in the
    windows console, send to the thread and get some output to a text widget
    I've been using inside the thread for some visibility.

    Thanks so much, I am feeling much more confident using threads that just
    a few days ago.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From ted brown@21:1/5 to All on Tue Aug 31 12:43:35 2021
    On 8/31/2021 1:37 AM, EL wrote:
    ted brown <tedbrown888@gmail.com> wrote:

    Two things though, puts is my usual debugger, and on windows I can't get
    puts to go to a console (from inside a thread::create script) nor can I
    see any errors pop up. It seems the thread just quits silently if I mess up. >>
    How do you debug your threads?


    When you use tclsh on windows from a cmd or powershell rather than wish,
    the puts goes on the cmd/ps. That’s how I do it…

    Hmmm, interesting. I use a tclkit with gui and twapi that I get from
    Ashok. I don't even install tcl/tk on windows any longer.

    I tried several ways to rename and/or proc replace [puts] inside a tcl
    thread with no success. I have a collection of tools that use puts to
    list globals, widget trees, etc. But with no console, I don't yet have
    a way to run them.

    What I've just now tossed together is a putz command that creates a
    toplevel and a text widget. I'm pretty happy with how well Tk can run
    inside a thread, which was mentioned in that posting comparing processes
    to threads.


    But admittedly I don’t use the Threads package a lot, I usually work with threads in C/C++ in Tcl extensions. Just stumbled over condition variables
    in the Threads package documentation recently, and found out that it’s the same concept as with C++ std::condition_variable. Not really surprising, though, but I was surprised that they also exist at the Tcl level ;)



    I've mostly just used [exec] and localhost sockets for this in the past.
    Now I want to try my hand at threads.

    I'm hoping I can use condition variables to build my own layer on top of threads to mimic a lightweight process or "task". At the higher level,
    it will be sending argc/argv like data, implemented with thread::send
    scripts.

    In order for my "tasks" to safely run sequentially while being free to
    use update or vwait, I figure I need a work queue that I can atomically
    access on both ends, plus a way to wait when it's empty.

    Either mutexes and/or condition variables with tsv might be just the ticket.

    Years ago when I programmed the VAX computer, it had a set of atomic
    queue instructions, which I used to implement interrupt safe lists in C
    code. It should be fun to figure this out inside tcl/tk with threads.

    Thanks for the tips.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From js@21:1/5 to ted brown on Tue Aug 31 16:26:53 2021
    On 8/31/21 3:43 PM, ted brown wrote:

    I tried several ways to rename and/or proc replace [puts] inside a tcl
    thread with no success. I have a collection of tools that use puts to
    list globals, widget trees, etc.  But with no console, I don't yet have
    a way to run them.

    What I've just now tossed together is a putz command that creates a
    toplevel and a text widget. I'm pretty happy with how well Tk can run
    inside a thread, which was mentioned in that posting comparing processes
    to threads.


    I was fortunate to learn about threads from a mentor several years ago
    and he had a nice trick for this that I have been using almost verbatim
    since (this is from memory so hopefully no embarrassing mistakes):

    In the main app:
    proc got_puts_from_sub {msg} {
    puts "$msg"
    flush stdout
    }

    In the sub-thread:
    proc puts_sub {msg} {
    thread::send -async MAIN_INTERP_ID [list got_puts_from_sub $msg]
    }

    Then, in the sub-thread, whenever I need to use puts, I simply use
    puts_sub. You can replace MAIN_INTER_ID with the actual parent interp's
    thread id in several ways.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From ted brown@21:1/5 to All on Tue Aug 31 13:52:06 2021
    On 8/31/2021 1:26 PM, js wrote:
    On 8/31/21 3:43 PM, ted brown wrote:

    I tried several ways to rename and/or proc replace [puts] inside a tcl
    thread with no success. I have a collection of tools that use puts to
    list globals, widget trees, etc.  But with no console, I don't yet
    have a way to run them.

    What I've just now tossed together is a putz command that creates a
    toplevel and a text widget. I'm pretty happy with how well Tk can run
    inside a thread, which was mentioned in that posting comparing
    processes to threads.


    I was fortunate to learn about threads from a mentor several years ago
    and he had a nice trick for this that I have been using almost verbatim
    since (this is from memory so hopefully no embarrassing mistakes):

    In the main app:
    proc got_puts_from_sub {msg} {
      puts "$msg"
    flush stdout
    }

    In the sub-thread:
    proc puts_sub {msg} {
      thread::send -async MAIN_INTERP_ID [list got_puts_from_sub  $msg]
    }

    Then, in the sub-thread, whenever I need to use puts, I simply use
    puts_sub. You can replace MAIN_INTER_ID with the actual parent interp's thread id in several ways.


    Interesting, I have a piece of code that was from heinrichmartin that
    will use info body/args/default to retrieve the full text of a proc that
    can then be changed (using regsub) and then eval'd to change a puts to something else, maybe a puts_sub, for code I then "import" into the
    thread. I Love tcl's dynamic capabilities.

    Then I just need a way to issue commands. Probably I'll just use a text
    entry widget inside the thread. Then I'll have all my 2 letter l?
    commands I use for listing globals, arrays, dicts, etc.

    Thanks.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From ted brown@21:1/5 to ted brown on Tue Aug 31 21:04:25 2021
    On 8/31/2021 12:43 PM, ted brown wrote:
    On 8/31/2021 1:37 AM, EL wrote:
    ted brown <tedbrown888@gmail.com> wrote:
    I'm hoping I can use condition variables to build my own layer on top of threads


    I found a perfect example in Ashok's book "The Tcl programming
    language". Section 22.11.2 on condition variables has as its example,
    just what I need, with a condition variable, mutex, and a shared
    variable list. Made to order!

    Thanks all.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From EL@21:1/5 to ted brown on Wed Sep 1 17:59:35 2021
    On 01.09.2021 06:04, ted brown wrote:
    On 8/31/2021 12:43 PM, ted brown wrote:
    On 8/31/2021 1:37 AM, EL wrote:
    ted brown <tedbrown888@gmail.com> wrote:
    I'm hoping I can use condition variables to build my own layer on top
    of threads


    I found a perfect example in Ashok's book "The Tcl programming
    language". Section 22.11.2 on condition variables has as its example,
    just what I need, with a condition variable, mutex, and a shared
    variable list. Made to order!


    Ashok has also created a promise module and wrote some articles about it:

    https://tcl-promise.magicsplat.com/ https://www.magicsplat.com/blog/tags/promises/

    If your objective is to just run some code in parallel from the main
    thread and wait for the results of these computations, then you might
    find this interesting as well.


    --
    EL

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From ted brown@21:1/5 to All on Wed Sep 1 14:44:24 2021
    On 9/1/2021 8:59 AM, EL wrote:
    On 01.09.2021 06:04, ted brown wrote:
    On 8/31/2021 12:43 PM, ted brown wrote:
    On 8/31/2021 1:37 AM, EL wrote:
    ted brown <tedbrown888@gmail.com> wrote:
    I'm hoping I can use condition variables to build my own layer on top
    of threads


    I found a perfect example in Ashok's book "The Tcl programming
    language". Section 22.11.2 on condition variables has as its example,
    just what I need, with a condition variable, mutex, and a shared
    variable list. Made to order!


    Ashok has also created a promise module and wrote some articles about it:

    https://tcl-promise.magicsplat.com/ https://www.magicsplat.com/blog/tags/promises/

    If your objective is to just run some code in parallel from the main
    thread and wait for the results of these computations, then you might
    find this interesting as well.




    Thanks for the suggestion. I have already looked into promises. In a
    sense, I'm attempting to create my own threads abstraction layer but
    with a different goal. Mine is maximal simplicity.

    My objective is mostly to learn to use tcl threads. My usual approach is
    to invent a small project using the tool I wish to learn. The project
    need not be useful.

    So, I've set myself to inventing what I'll call a task. It is a way to
    call a proc in its own thread, and either wait for it, or catch up to it
    later using a [bind] like mechanism. It will only support some common
    uses for threads.

    I don't know if I will succeed; if I do, I'll create a wiki page. If
    not, well I should have learned something about tcl threads.



    Here's my set of primitives so far:

    #################################
    #
    # From a parent thread - "name" for building tsv shared variables
    #
    # set taskid [task name prefixlist script]
    # set result [tcall $taskid ?-async? ?arg..?]
    # tbind $taskid [list callback_prefix ... ]
    #
    # inside a task
    #
    # twait args-var ;#waits for work to do and get args
    # treturn ?-exit? result ;# if no -exit, should be in a loop
    #
    #################################

    What might this be used for? Say you want to solve the common problem of
    a heavy compute proc, but want the gui to remain responsive, for example:

    proc heavy {args} {
    ...
    return $result
    }


    Something like this should do the trick to thread-ify heavy:

    #--------------
    set heavy_id [task heavy_task {heavy ...} {

    twait argv ; lassign $argv arg1 arg2 ... ;# wait till called, get args
    set result [heavy $arg1 $arg2 ....] ;# heavy was imported
    treturn -exit $result

    }
    #--------------

    and the main (or parent) thread would do this:

    set result [tcall $heavy_id arg1 .....]

    This will create a thread and retrieve [heavy] using something similar
    to Ashok's "reconstruct" proc. It then calls it synchronously.

    Anyway, that's as far as I've got, and have only started coding the
    [task] call, but have the reconstruct working nicely.

    The queue and condition variable example would be used behind the scenes
    so that multiple calls would queue up and run sequentially. Adding a
    loop around the 3 lines and removing the -exit turns it into a
    client/server thread. All the complex queue stuff remains hidden.

    Only building it and trying it out will determine if it's of any use.
    The journey is more important than the destination however.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)