~malept/ubuntu/lucid/python2.6/dev-dependency-fix

« back to all changes in this revision

Viewing changes to Demo/threads/Coroutine.py

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2009-02-13 12:51:00 UTC
  • Revision ID: james.westby@ubuntu.com-20090213125100-uufgcb9yeqzujpqw
Tags: upstream-2.6.1
ImportĀ upstreamĀ versionĀ 2.6.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Coroutine implementation using Python threads.
 
2
#
 
3
# Combines ideas from Guido's Generator module, and from the coroutine
 
4
# features of Icon and Simula 67.
 
5
#
 
6
# To run a collection of functions as coroutines, you need to create
 
7
# a Coroutine object to control them:
 
8
#    co = Coroutine()
 
9
# and then 'create' a subsidiary object for each function in the
 
10
# collection:
 
11
#    cof1 = co.create(f1 [, arg1, arg2, ...]) # [] means optional,
 
12
#    cof2 = co.create(f2 [, arg1, arg2, ...]) #... not list
 
13
#    cof3 = co.create(f3 [, arg1, arg2, ...])
 
14
# etc.  The functions need not be distinct; 'create'ing the same
 
15
# function multiple times gives you independent instances of the
 
16
# function.
 
17
#
 
18
# To start the coroutines running, use co.tran on one of the create'd
 
19
# functions; e.g., co.tran(cof2).  The routine that first executes
 
20
# co.tran is called the "main coroutine".  It's special in several
 
21
# respects:  it existed before you created the Coroutine object; if any of
 
22
# the create'd coroutines exits (does a return, or suffers an unhandled
 
23
# exception), EarlyExit error is raised in the main coroutine; and the
 
24
# co.detach() method transfers control directly to the main coroutine
 
25
# (you can't use co.tran() for this because the main coroutine doesn't
 
26
# have a name ...).
 
27
#
 
28
# Coroutine objects support these methods:
 
29
#
 
30
# handle = .create(func [, arg1, arg2, ...])
 
31
#    Creates a coroutine for an invocation of func(arg1, arg2, ...),
 
32
#    and returns a handle ("name") for the coroutine so created.  The
 
33
#    handle can be used as the target in a subsequent .tran().
 
34
#
 
35
# .tran(target, data=None)
 
36
#    Transfer control to the create'd coroutine "target", optionally
 
37
#    passing it an arbitrary piece of data. To the coroutine A that does
 
38
#    the .tran, .tran acts like an ordinary function call:  another
 
39
#    coroutine B can .tran back to it later, and if it does A's .tran
 
40
#    returns the 'data' argument passed to B's tran.  E.g.,
 
41
#
 
42
#    in coroutine coA   in coroutine coC    in coroutine coB
 
43
#      x = co.tran(coC)   co.tran(coB)        co.tran(coA,12)
 
44
#      print x # 12
 
45
#
 
46
#    The data-passing feature is taken from Icon, and greatly cuts
 
47
#    the need to use global variables for inter-coroutine communication.
 
48
#
 
49
# .back( data=None )
 
50
#    The same as .tran(invoker, data=None), where 'invoker' is the
 
51
#    coroutine that most recently .tran'ed control to the coroutine
 
52
#    doing the .back.  This is akin to Icon's "&source".
 
53
#
 
54
# .detach( data=None )
 
55
#    The same as .tran(main, data=None), where 'main' is the
 
56
#    (unnameable!) coroutine that started it all.  'main' has all the
 
57
#    rights of any other coroutine:  upon receiving control, it can
 
58
#    .tran to an arbitrary coroutine of its choosing, go .back to
 
59
#    the .detach'er, or .kill the whole thing.
 
60
#
 
61
# .kill()
 
62
#    Destroy all the coroutines, and return control to the main
 
63
#    coroutine.  None of the create'ed coroutines can be resumed after a
 
64
#    .kill().  An EarlyExit exception does a .kill() automatically.  It's
 
65
#    a good idea to .kill() coroutines you're done with, since the
 
66
#    current implementation consumes a thread for each coroutine that
 
67
#    may be resumed.
 
68
 
 
69
import thread
 
70
import sync
 
71
 
 
72
class _CoEvent:
 
73
    def __init__(self, func):
 
74
        self.f = func
 
75
        self.e = sync.event()
 
76
 
 
77
    def __repr__(self):
 
78
        if self.f is None:
 
79
            return 'main coroutine'
 
80
        else:
 
81
            return 'coroutine for func ' + self.f.func_name
 
82
 
 
83
    def __hash__(self):
 
84
        return id(self)
 
85
 
 
86
    def __cmp__(x,y):
 
87
        return cmp(id(x), id(y))
 
88
 
 
89
    def resume(self):
 
90
        self.e.post()
 
91
 
 
92
    def wait(self):
 
93
        self.e.wait()
 
94
        self.e.clear()
 
95
 
 
96
class Killed(Exception): pass
 
97
class EarlyExit(Exception): pass
 
98
 
 
99
class Coroutine:
 
100
    def __init__(self):
 
101
        self.active = self.main = _CoEvent(None)
 
102
        self.invokedby = {self.main: None}
 
103
        self.killed = 0
 
104
        self.value  = None
 
105
        self.terminated_by = None
 
106
 
 
107
    def create(self, func, *args):
 
108
        me = _CoEvent(func)
 
109
        self.invokedby[me] = None
 
110
        thread.start_new_thread(self._start, (me,) + args)
 
111
        return me
 
112
 
 
113
    def _start(self, me, *args):
 
114
        me.wait()
 
115
        if not self.killed:
 
116
            try:
 
117
                try:
 
118
                    apply(me.f, args)
 
119
                except Killed:
 
120
                    pass
 
121
            finally:
 
122
                if not self.killed:
 
123
                    self.terminated_by = me
 
124
                    self.kill()
 
125
 
 
126
    def kill(self):
 
127
        if self.killed:
 
128
            raise TypeError, 'kill() called on dead coroutines'
 
129
        self.killed = 1
 
130
        for coroutine in self.invokedby.keys():
 
131
            coroutine.resume()
 
132
 
 
133
    def back(self, data=None):
 
134
        return self.tran( self.invokedby[self.active], data )
 
135
 
 
136
    def detach(self, data=None):
 
137
        return self.tran( self.main, data )
 
138
 
 
139
    def tran(self, target, data=None):
 
140
        if not self.invokedby.has_key(target):
 
141
            raise TypeError, '.tran target %r is not an active coroutine' % (target,)
 
142
        if self.killed:
 
143
            raise TypeError, '.tran target %r is killed' % (target,)
 
144
        self.value = data
 
145
        me = self.active
 
146
        self.invokedby[target] = me
 
147
        self.active = target
 
148
        target.resume()
 
149
 
 
150
        me.wait()
 
151
        if self.killed:
 
152
            if self.main is not me:
 
153
                raise Killed
 
154
            if self.terminated_by is not None:
 
155
                raise EarlyExit, '%r terminated early' % (self.terminated_by,)
 
156
 
 
157
        return self.value
 
158
 
 
159
# end of module