ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Guard/README
Revision: 1.3
Committed: Sat Dec 13 21:47:07 2008 UTC (15 years, 5 months ago) by root
Branch: MAIN
CVS Tags: rel-0_5
Changes since 1.2: +53 -25 lines
Log Message:
0.5

File Contents

# Content
1 NAME
2 Guard - safe cleanup blocks
3
4 SYNOPSIS
5 use Guard;
6
7 # temporarily chdir to "/etc" directory, but make sure
8 # to go back to "/" no matter how myfun exits:
9 sub myfun {
10 scope_guard { chdir "/" };
11 chdir "/etc";
12
13 call_function_that_might_die_or_other_fun_stuff;
14 }
15
16 DESCRIPTION
17 This module implements so-called "guards". A guard is something (usually
18 an object) that "guards" a resource, ensuring that it is cleaned up when
19 expected.
20
21 Specifically, this module supports two different types of guards: guard
22 objects, which execute a given code block when destroyed, and scoped
23 guards, which are tied to the scope exit.
24
25 FUNCTIONS
26 This module currently exports the "scope_guard" and "guard" functions by
27 default.
28
29 scope_guard BLOCK
30 Registers a block that is executed when the current scope (block,
31 function, method, eval etc.) is exited.
32
33 See the EXCEPTIONS section for an explanation of how exceptions
34 (i.e. "die") are handled inside guard blocks.
35
36 The description below sounds a bit complicated, but that's just
37 because "scope_guard" tries to get even corner cases "right": the
38 goal is to provide you with a rock solid clean up tool.
39
40 The behaviour is similar to this code fragment:
41
42 eval ... code following scope_guard ...
43 {
44 local $@;
45 eval BLOCK;
46 eval { $Guard::DIED->() } if $@;
47 }
48 die if $@;
49
50 Except it is much faster, and the whole thing gets executed even
51 when the BLOCK calls "exit", "goto", "last" or escapes via other
52 means.
53
54 If multiple BLOCKs are registered to the same scope, they will be
55 executed in reverse order. Other scope-related things such as
56 "local" are managed via the same mechanism, so variables "local"ised
57 *after* calling "scope_guard" will be restored when the guard runs.
58
59 Example: temporarily change the timezone for the current process,
60 ensuring it will be reset when the "if" scope is exited:
61
62 use Guard;
63 use POSIX ();
64
65 if ($need_to_switch_tz) {
66 # make sure we call tzset after $ENV{TZ} has been restored
67 scope_guard { POSIX::tzset };
68
69 # localise after the scope_guard, so it gets undone in time
70 local $ENV{TZ} = "Europe/London";
71 POSIX::tzset;
72
73 # do something with the new timezone
74 }
75
76 my $guard = guard BLOCK
77 Behaves the same as "scope_guard", except that instead of executing
78 the block on scope exit, it returns an object whose lifetime
79 determines when the BLOCK gets executed: when the last reference to
80 the object gets destroyed, the BLOCK gets executed as with
81 "scope_guard".
82
83 The returned object can be copied as many times as you want.
84
85 See the EXCEPTIONS section for an explanation of how exceptions
86 (i.e. "die") are handled inside guard blocks.
87
88 Example: acquire a Coro::Semaphore for a second by registering a
89 timer. The timer callback references the guard used to unlock it
90 again. (Please ignore the fact that "Coro::Semaphore" has a "guard"
91 method that does this already):
92
93 use Guard;
94 use AnyEvent;
95 use Coro::Semaphore;
96
97 my $sem = new Coro::Semaphore;
98
99 sub lock_for_a_second {
100 $sem->down;
101 my $guard = guard { $sem->up };
102
103 my $timer;
104 $timer = AnyEvent->timer (after => 1, sub {
105 # do something
106 undef $sem;
107 undef $timer;
108 });
109 }
110
111 The advantage of doing this with a guard instead of simply calling
112 "$sem->down" in the callback is that you can opt not to create the
113 timer, or your code can throw an exception before it can create the
114 timer, or you can create multiple timers or other event watchers and
115 only when the last one gets executed will the lock be unlocked.
116 Using the "guard", you do not have to worry about catching all the
117 places where you have to unlock the semaphore.
118
119 $guard->cancel
120 Calling this function will "disable" the guard object returned by
121 the "guard" function, i.e. it will free the BLOCK originally passed
122 to "guard "and will arrange for the BLOCK not to be executed.
123
124 This can be useful when you use "guard" to create a fatal cleanup
125 handler and later decide it is no longer needed.
126
127 EXCEPTIONS
128 Guard blocks should not normally throw exceptions (that is, "die").
129 After all, they are usually used to clean up after such exceptions.
130 However, if something truly exceptional is happening, a guard block
131 should be allowed to die. Also, programming errors are a large source of
132 exceptions, and the programmer certainly wants to know about those.
133
134 Since in most cases, the block executing when the guard gets executes
135 does not know or does not care about the guard blocks, it makes little
136 sense to let containing code handle the exception.
137
138 Therefore, whenever a guard block throws an exception, it will be
139 caught, and this module will call the code reference stored in
140 $Guard::DIED (with $@ set to the actual exception), which is similar to
141 how most event loops handle this case.
142
143 The default for $Guard::DIED is to call "warn "$@"".
144
145 The $@ variable will be restored to its value before the guard call in
146 all cases, so guards will not disturb $@ in any way.
147
148 The code reference stored in $Guard::DIED should not die (behaviour is
149 not guaranteed, but right now, the exception will simply be ignored).
150
151 AUTHOR
152 Marc Lehmann <schmorp@schmorp.de>
153 http://home.schmorp.de/
154
155 THANKS
156 Thanks to Marco Maisenhelder, who reminded me of the $Guard::DIED
157 solution to the problem of exceptions.
158