Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f4e13a4

Browse files
committed
Add 1994 Coroutine module by Tim Peters
1 parent 704925c commit f4e13a4

4 files changed

Lines changed: 332 additions & 0 deletions

File tree

Demo/threads/Coroutine.py

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
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+
Killed = 'Coroutine.Killed'
97+
EarlyExit = 'Coroutine.EarlyExit'
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 ' + `target` + \
142+
' is not an active coroutine'
143+
if self.killed:
144+
raise TypeError, '.tran target ' + `target` + ' is killed'
145+
self.value = data
146+
me = self.active
147+
self.invokedby[target] = me
148+
self.active = target
149+
target.resume()
150+
151+
me.wait()
152+
if self.killed:
153+
if self.main is not me:
154+
raise Killed
155+
if self.terminated_by is not None:
156+
raise EarlyExit, `self.terminated_by` + ' terminated early'
157+
158+
return self.value
159+
160+
# end of module

Demo/threads/README

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,7 @@ find.py Parallelized "find(1)" (looks for directories).
88
sync.py Condition variables primitives by Tim Peters.
99
telnet.py Version of ../sockets/telnet.py using threads.
1010
wpi.py Version of ../scripts/pi.py using threads (needs stdwin).
11+
12+
Coroutine.py Coroutines using threads, by Tim Peters (22 May 94)
13+
fcmp.py Example of above, by Tim
14+
squasher.py Another example of above, also by Tim

Demo/threads/fcmp.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Coroutine example: controlling multiple instances of a single function
2+
3+
from Coroutine import *
4+
5+
# fringe visits a nested list in inorder, and detaches for each non-list
6+
# element; raises EarlyExit after the list is exhausted
7+
def fringe( co, list ):
8+
for x in list:
9+
if type(x) is type([]):
10+
fringe(co, x)
11+
else:
12+
co.detach(x)
13+
14+
def printinorder( list ):
15+
co = Coroutine()
16+
f = co.create(fringe, co, list)
17+
try:
18+
while 1:
19+
print co.tran(f),
20+
except EarlyExit:
21+
pass
22+
print
23+
24+
printinorder([1,2,3]) # 1 2 3
25+
printinorder([[[[1,[2]]],3]]) # ditto
26+
x = [0, 1, [2, [3]], [4,5], [[[6]]] ]
27+
printinorder(x) # 0 1 2 3 4 5 6
28+
29+
# fcmp lexicographically compares the fringes of two nested lists
30+
def fcmp( l1, l2 ):
31+
co1 = Coroutine(); f1 = co1.create(fringe, co1, l1)
32+
co2 = Coroutine(); f2 = co2.create(fringe, co2, l2)
33+
while 1:
34+
try:
35+
v1 = co1.tran(f1)
36+
except EarlyExit:
37+
try:
38+
v2 = co2.tran(f2)
39+
except EarlyExit:
40+
return 0
41+
co2.kill()
42+
return -1
43+
try:
44+
v2 = co2.tran(f2)
45+
except EarlyExit:
46+
co1.kill()
47+
return 1
48+
if v1 != v2:
49+
co1.kill(); co2.kill()
50+
return cmp(v1,v2)
51+
52+
print fcmp(range(7), x) # 0; fringes are equal
53+
print fcmp(range(6), x) # -1; 1st list ends early
54+
print fcmp(x, range(6)) # 1; 2nd list ends early
55+
print fcmp(range(8), x) # 1; 2nd list ends early
56+
print fcmp(x, range(8)) # -1; 1st list ends early
57+
print fcmp([1,[[2],8]],
58+
[[[1],2],8]) # 0
59+
print fcmp([1,[[3],8]],
60+
[[[1],2],8]) # 1
61+
print fcmp([1,[[2],8]],
62+
[[[1],2],9]) # -1
63+
64+
# end of example

Demo/threads/squasher.py

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# Coroutine example: general coroutine transfers
2+
#
3+
# The program is a variation of a Simula 67 program due to Dahl & Hoare,
4+
# who in turn credit the original example to Conway.
5+
#
6+
# We have a number of input lines, terminated by a 0 byte. The problem
7+
# is to squash them together into output lines containing 72 characters
8+
# each. A semicolon must be added between input lines. Runs of blanks
9+
# and tabs in input lines must be squashed into single blanks.
10+
# Occurrences of "**" in input lines must be replaced by "^".
11+
#
12+
# Here's a test case:
13+
14+
test = """\
15+
d = sqrt(b**2 - 4*a*c)
16+
twoa = 2*a
17+
L = -b/twoa
18+
R = d/twoa
19+
A1 = L + R
20+
A2 = L - R\0
21+
"""
22+
23+
# The program should print:
24+
25+
# d = sqrt(b^2 - 4*a*c);twoa = 2*a; L = -b/twoa; R = d/twoa; A1 = L + R;
26+
#A2 = L - R
27+
#done
28+
29+
# getline: delivers the next input line to its invoker
30+
# disassembler: grabs input lines from getline, and delivers them one
31+
# character at a time to squasher, also inserting a semicolon into
32+
# the stream between lines
33+
# squasher: grabs characters from disassembler and passes them on to
34+
# assembler, first replacing "**" with "^" and squashing runs of
35+
# whitespace
36+
# assembler: grabs characters from squasher and packs them into lines
37+
# with 72 character each, delivering each such line to putline;
38+
# when it sees a null byte, passes the last line to putline and
39+
# then kills all the coroutines
40+
# putline: grabs lines from assembler, and just prints them
41+
42+
from Coroutine import *
43+
44+
def getline(text):
45+
for line in string.splitfields(text, '\n'):
46+
co.back(line)
47+
48+
def disassembler():
49+
while 1:
50+
card = co.tran(cogetline)
51+
for i in range(len(card)):
52+
co.tran(cosquasher, card[i])
53+
co.tran(cosquasher, ';')
54+
55+
def squasher():
56+
while 1:
57+
ch = co.tran(codisassembler)
58+
if ch == '*':
59+
ch2 = co.tran(codisassembler)
60+
if ch2 == '*':
61+
ch = '^'
62+
else:
63+
co.tran(coassembler, ch)
64+
ch = ch2
65+
if ch in ' \t':
66+
while 1:
67+
ch2 = co.tran(codisassembler)
68+
if ch2 not in ' \t':
69+
break
70+
co.tran(coassembler, ' ')
71+
ch = ch2
72+
co.tran(coassembler, ch)
73+
74+
def assembler():
75+
line = ''
76+
while 1:
77+
ch = co.tran(cosquasher)
78+
if ch == '\0':
79+
break
80+
if len(line) == 72:
81+
co.tran(coputline, line)
82+
line = ''
83+
line = line + ch
84+
line = line + ' ' * (72 - len(line))
85+
co.tran(coputline, line)
86+
co.kill()
87+
88+
def putline():
89+
while 1:
90+
line = co.tran(coassembler)
91+
print line
92+
93+
import string
94+
co = Coroutine()
95+
cogetline = co.create(getline, test)
96+
coputline = co.create(putline)
97+
coassembler = co.create(assembler)
98+
codisassembler = co.create(disassembler)
99+
cosquasher = co.create(squasher)
100+
101+
co.tran(coputline)
102+
print 'done'
103+
104+
# end of example

0 commit comments

Comments
 (0)