… | |
… | |
11 | This module implements a few functions from the Storable module in a way |
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 |
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 |
13 | game server) sometimes need to load large Storable objects without |
14 | blocking the server for a long time. |
14 | blocking the server for a long time. |
15 | |
15 | |
|
|
16 | 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 | |
16 | As it seems that Storable is not reentrant, this module also serialises |
20 | As it seems that Storable is not reentrant, this module also serialises |
17 | calls to freeze and thaw between coroutines. |
21 | calls to freeze and thaw between coroutines as necessary (for this to work |
|
|
22 | reliably you always have to use this module, however). |
18 | |
23 | |
19 | =head1 FUNCTIONS |
24 | =head1 FUNCTIONS |
20 | |
25 | |
21 | =over 4 |
26 | =over 4 |
22 | |
27 | |
… | |
… | |
29 | This works by calling C<Coro::cede> for every 4096 bytes read in. |
34 | This works by calling C<Coro::cede> for every 4096 bytes read in. |
30 | |
35 | |
31 | =item $pst = freeze $ref |
36 | =item $pst = freeze $ref |
32 | |
37 | |
33 | Freeze the given scalar into a Storable object. It uses the same format as |
38 | Freeze the given scalar into a Storable object. It uses the same format as |
34 | C<Storable::nstore_fd> (note the C<n>). |
39 | C<Storable::store_fd>. |
35 | |
40 | |
36 | This works by calling C<Coro::cede> for every write that Storable |
41 | This works by calling C<Coro::cede> for every write that Storable |
37 | issues. Unfortunately, Storable often makes many very small writes, so it |
42 | issues. Unfortunately, Storable often makes many very small writes, so it |
38 | is rather inefficient. But it does keep the latency low. |
43 | is rather inefficient. But it does keep the latency low. |
|
|
44 | |
|
|
45 | =item $pst = nfreeze $ref |
|
|
46 | |
|
|
47 | Same as C<freeze> but is compatible to C<Storabel:.nstore_fd> (note the |
|
|
48 | 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 | C<Storable::freeze> for this as Storabel uses incompatible formats for |
|
|
56 | memory and file images. |
|
|
57 | |
|
|
58 | =item $pst = blocking_nfreeze $ref |
|
|
59 | |
|
|
60 | Same as C<blocking_freeze> but uses C<nfreeze> internally. |
39 | |
61 | |
40 | =back |
62 | =back |
41 | |
63 | |
42 | =cut |
64 | =cut |
43 | |
65 | |
… | |
… | |
49 | use Coro::Semaphore (); |
71 | use Coro::Semaphore (); |
50 | |
72 | |
51 | use Storable; |
73 | use Storable; |
52 | use base "Exporter"; |
74 | use base "Exporter"; |
53 | |
75 | |
54 | our $VERSION = '0.1'; |
76 | our $VERSION = '0.2'; |
55 | our @EXPORT = qw(freeze thaw); |
77 | our @EXPORT = qw(thaw freeze nfreeze blocking_freeze blocking_nfreeze); |
56 | |
78 | |
57 | my $lock = new Coro::Semaphore; |
79 | my $lock = new Coro::Semaphore; |
58 | |
|
|
59 | sub freeze($) { |
|
|
60 | my $guard = $lock->guard; |
|
|
61 | |
|
|
62 | open my $fh, ">:via(CoroCede)", \my $buf |
|
|
63 | or die "cannot open pst via CoroCede: $!"; |
|
|
64 | Storable::nstore_fd $_[0], $fh; |
|
|
65 | $buf |
|
|
66 | } |
|
|
67 | |
80 | |
68 | sub thaw($) { |
81 | sub thaw($) { |
69 | my $guard = $lock->guard; |
82 | my $guard = $lock->guard; |
70 | |
83 | |
71 | open my $fh, "<:via(CoroCede)", \$_[0] |
84 | open my $fh, "<:via(CoroCede)", \$_[0] |
72 | or die "cannot open pst via CoroCede: $!"; |
85 | or die "cannot open pst via CoroCede: $!"; |
73 | Storable::fd_retrieve $fh |
86 | Storable::fd_retrieve $fh |
74 | } |
87 | } |
75 | |
88 | |
|
|
89 | sub freeze($) { |
|
|
90 | my $guard = $lock->guard; |
|
|
91 | |
|
|
92 | open my $fh, ">:via(CoroCede)", \my $buf |
|
|
93 | or die "cannot open pst via CoroCede: $!"; |
|
|
94 | Storable::store_fd $_[0], $fh; |
|
|
95 | $buf |
|
|
96 | } |
|
|
97 | |
|
|
98 | sub nfreeze($) { |
|
|
99 | my $guard = $lock->guard; |
|
|
100 | |
|
|
101 | open my $fh, ">:via(CoroCede)", \my $buf |
|
|
102 | or die "cannot open pst via CoroCede: $!"; |
|
|
103 | Storable::nstore_fd $_[0], $fh; |
|
|
104 | $buf |
|
|
105 | } |
|
|
106 | |
|
|
107 | sub blocking_freeze { |
|
|
108 | open my $fh, ">", \my $buf |
|
|
109 | or die "cannot open pst: $!"; |
|
|
110 | Storable::store_fd $_[0], $fh; |
|
|
111 | close $fh; |
|
|
112 | |
|
|
113 | $buf |
|
|
114 | } |
|
|
115 | |
|
|
116 | sub blocking_nfreeze { |
|
|
117 | open my $fh, ">", \my $buf |
|
|
118 | or die "cannot open pst: $!"; |
|
|
119 | Storable::nstore_fd $_[0], $fh; |
|
|
120 | close $fh; |
|
|
121 | |
|
|
122 | $buf |
|
|
123 | } |
|
|
124 | |
76 | package PerlIO::via::CoroCede; |
125 | package PerlIO::via::CoroCede; |
77 | |
126 | |
78 | # generic cede-on-read/write filtering layer |
127 | # generic cede-on-read/write filtering layer |
|
|
128 | |
|
|
129 | use Time::HiRes ("time"); |
|
|
130 | |
|
|
131 | our $GRANULARITY = 0.001; |
|
|
132 | |
|
|
133 | my $next_cede; |
79 | |
134 | |
80 | sub PUSHED { |
135 | sub PUSHED { |
81 | __PACKAGE__ |
136 | __PACKAGE__ |
82 | } |
137 | } |
83 | |
138 | |
84 | sub FILL { |
139 | sub FILL { |
|
|
140 | if ($next_cede <= time) { |
|
|
141 | $next_cede = time + $GRANULARITY; # calling time() twice usually is a net win |
85 | Coro::cede; |
142 | Coro::cede; |
|
|
143 | } |
|
|
144 | |
86 | read $_[1], my $buf, 512 |
145 | read $_[1], my $buf, 512 |
87 | or return undef; |
146 | or return undef; |
|
|
147 | |
88 | $buf |
148 | $buf |
89 | } |
149 | } |
90 | |
150 | |
91 | sub WRITE { |
151 | sub WRITE { |
|
|
152 | if ($next_cede <= (my $now = time)) { |
92 | Coro::cede; |
153 | Coro::cede; |
|
|
154 | $next_cede = $now + $GRANULARITY; |
|
|
155 | } |
|
|
156 | |
93 | (print {$_[2]} $_[1]) ? length $_[1] : -1 |
157 | (print {$_[2]} $_[1]) ? length $_[1] : -1 |
94 | } |
158 | } |
95 | |
159 | |
96 | 1; |
160 | 1; |
97 | |
161 | |