ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Coro/Coro/Storable.pm
Revision: 1.16
Committed: Fri May 30 21:34:52 2008 UTC (16 years, 1 month ago) by root
Branch: MAIN
CVS Tags: rel-4_741
Changes since 1.15: +2 -2 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 =head1 NAME
2    
3     Coro::Storable - offer a more fine-grained Storable interface
4    
5     =head1 SYNOPSIS
6    
7     use Coro::Storable;
8    
9     =head1 DESCRIPTION
10    
11     This module implements a few functions from the Storable module in a way
12     so that it cede's more often. Some applications (such as the Crossfire
13     game server) sometimes need to load large Storable objects without
14     blocking the server for a long time.
15    
16 root 1.6 This is being implemented by using a perlio layer that feeds only small
17     amounts of data (512 bytes per call) into Storable, and C<Coro::cede>'ing
18     regularly (at most 1000 times per second by default, though).
19    
20 root 1.2 As it seems that Storable is not reentrant, this module also serialises
21 root 1.6 calls to freeze and thaw between coroutines as necessary (for this to work
22     reliably you always have to use this module, however).
23 root 1.2
24 root 1.1 =head1 FUNCTIONS
25    
26     =over 4
27    
28     =item $ref = thaw $pst
29    
30     Retrieve an object from the given $pst, which must have been created with
31     C<Coro::Storable::freeze> or C<Storable::store_fd>/C<Storable::store>
32     (sorry, but Storable uses incompatible formats for disk/mem objects).
33    
34 root 1.4 This works by calling C<Coro::cede> for every 4096 bytes read in.
35 root 1.1
36     =item $pst = freeze $ref
37    
38     Freeze the given scalar into a Storable object. It uses the same format as
39 root 1.6 C<Storable::store_fd>.
40 root 1.1
41     This works by calling C<Coro::cede> for every write that Storable
42     issues. Unfortunately, Storable often makes many very small writes, so it
43     is rather inefficient. But it does keep the latency low.
44    
45 root 1.6 =item $pst = nfreeze $ref
46    
47 root 1.7 Same as C<freeze> but is compatible to C<Storable::nstore_fd> (note the
48 root 1.6 C<n>).
49    
50     =item $pst = blocking_freeze $ref
51    
52     Same as C<freeze> but is guaranteed to block. This is useful e.g. in
53     C<Coro::Util::fork_eval> when you want to serialise a data structure
54     for use with the C<thaw> function for this module. You cannot use
55 root 1.7 C<Storable::freeze> for this as Storable uses incompatible formats for
56 root 1.6 memory and file images.
57    
58     =item $pst = blocking_nfreeze $ref
59    
60     Same as C<blocking_freeze> but uses C<nfreeze> internally.
61    
62 root 1.8 =item $guard = guard;
63    
64     Acquire the Storable lock, for when you want to call Storable yourself.
65    
66 root 1.1 =back
67    
68     =cut
69    
70     package Coro::Storable;
71    
72     use strict;
73 root 1.13 no warnings;
74 root 1.1
75     use Coro ();
76 root 1.2 use Coro::Semaphore ();
77 root 1.1
78 root 1.13 BEGIN {
79     # suppress warnings
80     local $^W = 0;
81     require Storable;
82     }
83    
84 root 1.1 use Storable;
85     use base "Exporter";
86    
87 root 1.16 our $VERSION = 4.741;
88 root 1.9 our @EXPORT = qw(thaw freeze nfreeze blocking_thaw blocking_freeze blocking_nfreeze);
89 root 1.1
90 root 1.2 my $lock = new Coro::Semaphore;
91    
92 root 1.8 sub guard {
93     $lock->guard
94     }
95    
96 root 1.6 sub thaw($) {
97     my $guard = $lock->guard;
98    
99     open my $fh, "<:via(CoroCede)", \$_[0]
100     or die "cannot open pst via CoroCede: $!";
101     Storable::fd_retrieve $fh
102     }
103    
104 root 1.1 sub freeze($) {
105 root 1.2 my $guard = $lock->guard;
106    
107 root 1.1 open my $fh, ">:via(CoroCede)", \my $buf
108     or die "cannot open pst via CoroCede: $!";
109 root 1.6 Storable::store_fd $_[0], $fh;
110 root 1.1 $buf
111     }
112    
113 root 1.6 sub nfreeze($) {
114 root 1.2 my $guard = $lock->guard;
115    
116 root 1.6 open my $fh, ">:via(CoroCede)", \my $buf
117 root 1.1 or die "cannot open pst via CoroCede: $!";
118 root 1.6 Storable::nstore_fd $_[0], $fh;
119     $buf
120     }
121    
122 root 1.10 sub blocking_thaw($) {
123 root 1.9 my $guard = $lock->guard;
124    
125     open my $fh, "<", \$_[0]
126     or die "cannot open pst: $!";
127     Storable::fd_retrieve $fh
128     }
129    
130 root 1.10 sub blocking_freeze($) {
131 root 1.8 my $guard = $lock->guard;
132    
133 root 1.6 open my $fh, ">", \my $buf
134     or die "cannot open pst: $!";
135     Storable::store_fd $_[0], $fh;
136     close $fh;
137    
138     $buf
139     }
140    
141 root 1.10 sub blocking_nfreeze($) {
142 root 1.8 my $guard = $lock->guard;
143    
144 root 1.6 open my $fh, ">", \my $buf
145     or die "cannot open pst: $!";
146     Storable::nstore_fd $_[0], $fh;
147     close $fh;
148    
149     $buf
150 root 1.1 }
151    
152     package PerlIO::via::CoroCede;
153    
154     # generic cede-on-read/write filtering layer
155    
156 root 1.6 use Time::HiRes ("time");
157    
158     our $GRANULARITY = 0.001;
159    
160     my $next_cede;
161    
162 root 1.1 sub PUSHED {
163     __PACKAGE__
164     }
165    
166     sub FILL {
167 root 1.6 if ($next_cede <= time) {
168     $next_cede = time + $GRANULARITY; # calling time() twice usually is a net win
169 root 1.16 Coro::cede ();
170 root 1.6 }
171    
172 root 1.5 read $_[1], my $buf, 512
173 root 1.3 or return undef;
174 root 1.6
175 root 1.1 $buf
176     }
177    
178     sub WRITE {
179 root 1.6 if ($next_cede <= (my $now = time)) {
180     Coro::cede;
181     $next_cede = $now + $GRANULARITY;
182     }
183    
184 root 1.1 (print {$_[2]} $_[1]) ? length $_[1] : -1
185     }
186    
187     1;
188    
189     =head1 AUTHOR
190    
191     Marc Lehmann <schmorp@schmorp.de>
192     http://home.schmorp.de/
193    
194     =cut
195    
196