File: threads.tcl

package info (click to toggle)
moodss 19.7-1
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 6,136 kB
  • ctags: 3,149
  • sloc: tcl: 49,048; ansic: 187; perl: 178; makefile: 166; sh: 109; python: 65
file content (96 lines) | stat: -rw-r--r-- 3,983 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# copyright (C) 1997-2005 Jean-Luc Fontaine (mailto:jfontain@free.fr)
# this program is free software: please read the COPYRIGHT file enclosed in this package or use the Help Copyright menu

# $Id: threads.tcl,v 1.15 2005/01/02 00:45:07 jfontain Exp $


package present Thread 2.5
package provide threads [lindex {$Revision: 1.15 $} 1]


namespace eval threads {

    proc error {thread information} {
        variable errorScript

        if {[info exists errorScript($thread)]} {
            uplevel #0 $errorScript($thread) [list $information]                                    ;# always invoke at global level
        } else {
            puts stderr $information
            exit 1
        }
    }

    thread::errorproc ::threads::error

    proc catchError {thread script} {
        variable errorScript

        if {[string length $script] == 0} {
            catch {unset errorScript($thread)}
        } else {
            set errorScript($thread) $script
        }
    }

}


class worker {

    proc worker {this} {
        set ($this,thread) [thread::create -preserved]                     ;# so that thread can be released in a controlled fashion
        threads::catchError $($this,thread) "worker::error $this"
    }

    proc ~worker {this} {
        thread::release $($this,thread)
        threads::catchError $($this,thread) {}
    }

    proc error {this information} {
        set ($this,information) $information         ;# there was an error during the script invocation: store the error information
    }

    proc command {this args} {                                                                    ;# ignore trace callback arguments
        set result $($this,result)                                  ;# the script result if there was no error, or the error message
        set information $($this,information)        ;# empty if there was no error, else the error information including stack trace
        unset ($this,result) ($this,information)
        uplevel #0 $($this,command) [list $result] [list $information]                              ;# always invoke at global level
    }

    # public procedure: evaluate script in background (unblocks), wait till completion, can be caught as a regular procedure
    proc wait {this script} {
        if {[info exists ($this,information)]} {
            ::error {scripts queuing not implemented}
        }
        set ($this,information) {}
        thread::send -async $($this,thread) $script worker::($this,data)     ;# do not use result member as it interferes with trace
        vwait worker::($this,data)
        set result $($this,data)
        set information $($this,information)
        unset ($this,data) ($this,information)
        if {[string length $information] > 0} {                                                                ;# there was an error
            ::error $result                                                                   ;# variable contains the error message
        } else {
            return $result
        }
    }

    # public procedure:
    # let the thread invoke the script, in background with callback if command defined
    # to invoke in background while ignoring the result, use Tcl list command: evaluate $worker $script list
    proc evaluate {this script {command {}}} {
        if {[info exists ($this,information)]} {
            ::error {scripts queuing not implemented}
        }
        if {[string length $command] == 0} {                                                             ;# no background invocation
            return [thread::send $($this,thread) $script]                                                    ;# immediate invocation
        }
        set ($this,information) {}
        set ($this,command) $command
        thread::send -async $($this,thread) $script worker::($this,result)                                  ;# background invocation
        trace variable worker::($this,result) w "worker::command $this"
    }

}