ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Coro/Coro.pm
Revision: 1.15
Committed: Tue Jul 17 02:55:29 2001 UTC (22 years, 10 months ago) by root
Branch: MAIN
Changes since 1.14: +1 -1 lines
Log Message:
*** empty log message ***

File Contents

# Content
1 =head1 NAME
2
3 Coro - coroutine process abstraction
4
5 =head1 SYNOPSIS
6
7 use Coro;
8
9 async {
10 # some asynchronous thread of execution
11 };
12
13 # alternatively create an async process like this:
14
15 sub some_func : Coro {
16 # some more async code
17 }
18
19 yield;
20
21 =head1 DESCRIPTION
22
23 This module collection manages coroutines. Coroutines are similar to
24 Threads but don't run in parallel.
25
26 This module is still experimental, see the BUGS section below.
27
28 =cut
29
30 package Coro;
31
32 use Coro::State;
33
34 use base Exporter;
35
36 $VERSION = 0.06;
37
38 @EXPORT = qw(async yield schedule terminate);
39 @EXPORT_OK = qw($current);
40
41 {
42 use subs 'async';
43
44 my @async;
45
46 # this way of handling attributes simply is NOT scalable ;()
47 sub import {
48 Coro->export_to_level(1, @_);
49 my $old = *{(caller)[0]."::MODIFY_CODE_ATTRIBUTES"}{CODE};
50 *{(caller)[0]."::MODIFY_CODE_ATTRIBUTES"} = sub {
51 my ($package, $ref) = (shift, shift);
52 my @attrs;
53 for (@_) {
54 if ($_ eq "Coro") {
55 push @async, $ref;
56 } else {
57 push @attrs, @_;
58 }
59 }
60 return $old ? $old->($package, $name, @attrs) : @attrs;
61 };
62 }
63
64 sub INIT {
65 async pop @async while @async;
66 }
67 }
68
69 =item $main
70
71 This coroutine represents the main program.
72
73 =cut
74
75 our $main = new Coro;
76
77 =item $current
78
79 The current coroutine (the last coroutine switched to). The initial value is C<$main> (of course).
80
81 =cut
82
83 # maybe some other module used Coro::Specific before...
84 if ($current) {
85 $main->{specific} = $current->{specific};
86 }
87
88 our $current = $main;
89
90 =item $idle
91
92 The coroutine to switch to when no other coroutine is running. The default
93 implementation prints "FATAL: deadlock detected" and exits.
94
95 =cut
96
97 # should be done using priorities :(
98 our $idle = new Coro sub {
99 print STDERR "FATAL: deadlock detected\n";
100 exit(51);
101 };
102
103 # we really need priorities...
104 ## my @ready; #d#
105 our @ready = (); # the ready queue. hehe, rather broken ;)
106
107 # static methods. not really.
108
109 =head2 STATIC METHODS
110
111 Static methods are actually functions that operate on the current process only.
112
113 =over 4
114
115 =item async { ... } [@args...]
116
117 Create a new asynchronous process and return it's process object
118 (usually unused). When the sub returns the new process is automatically
119 terminated.
120
121 # create a new coroutine that just prints its arguments
122 async {
123 print "@_\n";
124 } 1,2,3,4;
125
126 The coderef you submit MUST NOT be a closure that refers to variables
127 in an outer scope. This does NOT work. Pass arguments into it instead.
128
129 =cut
130
131 sub async(&@) {
132 my $pid = new Coro @_;
133 $pid->ready;
134 $pid;
135 }
136
137 =item schedule
138
139 Calls the scheduler. Please note that the current process will not be put
140 into the ready queue, so calling this function usually means you will
141 never be called again.
142
143 =cut
144
145 my $prev;
146
147 sub schedule {
148 # should be done using priorities :(
149 ($prev, $current) = ($current, shift @ready || $idle);
150 Coro::State::transfer($prev, $current);
151 }
152
153 =item yield
154
155 Yield to other processes. This function puts the current process into the
156 ready queue and calls C<schedule>.
157
158 =cut
159
160 sub yield {
161 $current->ready;
162 &schedule;
163 }
164
165 =item terminate
166
167 Terminates the current process.
168
169 Future versions of this function will allow result arguments.
170
171 =cut
172
173 sub terminate {
174 $current->{_results} = [@_];
175 &schedule;
176 }
177
178 =back
179
180 # dynamic methods
181
182 =head2 PROCESS METHODS
183
184 These are the methods you can call on process objects.
185
186 =over 4
187
188 =item new Coro \&sub [, @args...]
189
190 Create a new process and return it. When the sub returns the process
191 automatically terminates. To start the process you must first put it into
192 the ready queue by calling the ready method.
193
194 The coderef you submit MUST NOT be a closure that refers to variables
195 in an outer scope. This does NOT work. Pass arguments into it instead.
196
197 =cut
198
199 sub _newcoro {
200 terminate &{+shift};
201 }
202
203 sub new {
204 my $class = shift;
205 bless {
206 _coro_state => (new Coro::State $_[0] && \&_newcoro, @_),
207 }, $class;
208 }
209
210 =item $process->ready
211
212 Put the current process into the ready queue.
213
214 =cut
215
216 sub ready {
217 push @ready, $_[0];
218 }
219
220 =back
221
222 =cut
223
224 1;
225
226 =head1 BUGS
227
228 - could be faster, especially when the core would introduce special
229 support for coroutines (like it does for threads).
230 - there is still a memleak on coroutine termination that I could not
231 identify. Could be as small as a single SV.
232 - this module is not well-tested.
233
234 =head1 SEE ALSO
235
236 L<Coro::Channel>, L<Coro::Cont>, L<Coro::Specific>, L<Coro::Semaphore>,
237 L<Coro::Signal>, L<Coro::State>, L<Coro::Event>.
238
239 =head1 AUTHOR
240
241 Marc Lehmann <pcg@goof.com>
242 http://www.goof.com/pcg/marc/
243
244 =cut
245