|
1 # Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 # All rights reserved. |
|
3 # This component and the accompanying materials are made available |
|
4 # under the terms of the License "Eclipse Public License v1.0" |
|
5 # which accompanies this distribution, and is available |
|
6 # at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 # |
|
8 # Initial Contributors: |
|
9 # Nokia Corporation - initial contribution. |
|
10 # |
|
11 # Contributors: |
|
12 # |
|
13 # Description: |
|
14 # |
|
15 # |
|
16 |
|
17 package CommandController; |
|
18 |
|
19 use strict; |
|
20 |
|
21 |
|
22 # |
|
23 # Constants. |
|
24 # |
|
25 |
|
26 use constant READER_SEMAPHORE_NAME => "CommandControllerReaderSemaphore_"; |
|
27 use constant WRITER_SEMAPHORE_NAME => "CommandControllerWriterSemaphore_"; |
|
28 use constant MAX_NUM_CONCURRENT_READERS => 100; |
|
29 use constant CMD_INDEPENDANT => 0; # Commands that can be run regardless of what else is running. |
|
30 use constant CMD_ENV_READER => 1; # Commands that only read the environment. |
|
31 use constant CMD_ENV_WRITER => 2; # Commands that modify the environment. |
|
32 |
|
33 my %commandInfo = ( |
|
34 EnvMembership => CMD_INDEPENDANT, |
|
35 CleanRemote => CMD_INDEPENDANT, |
|
36 ExportEnv => CMD_INDEPENDANT, |
|
37 ExportRel => CMD_INDEPENDANT, |
|
38 CopyRel => CMD_INDEPENDANT, |
|
39 ImportEnv => CMD_INDEPENDANT, |
|
40 ImportRel => CMD_INDEPENDANT, |
|
41 LatestVer => CMD_INDEPENDANT, |
|
42 PullEnv => CMD_INDEPENDANT, |
|
43 PushEnv => CMD_INDEPENDANT, |
|
44 PushRel => CMD_INDEPENDANT, |
|
45 PullRel => CMD_INDEPENDANT, |
|
46 DeltaEnv => CMD_INDEPENDANT, |
|
47 BinInfo => CMD_ENV_READER, |
|
48 SourceInfo => CMD_ENV_READER, |
|
49 DiffEnv => CMD_ENV_READER, |
|
50 DiffRel => CMD_ENV_READER, |
|
51 ModNotes => CMD_ENV_READER, |
|
52 ViewNotes => CMD_ENV_READER, |
|
53 BuildRel => CMD_ENV_READER, |
|
54 EnvSize => CMD_ENV_READER, |
|
55 MakeSnapShot => CMD_ENV_READER, |
|
56 CleanEnv => CMD_ENV_WRITER, |
|
57 EnvInfo => CMD_ENV_WRITER, |
|
58 GetEnv => CMD_ENV_WRITER, |
|
59 GetRel => CMD_ENV_WRITER, |
|
60 GetSource => CMD_ENV_WRITER, |
|
61 InstallSnapShot => CMD_ENV_WRITER, |
|
62 MakeEnv => CMD_ENV_WRITER, |
|
63 MakeRel => CMD_ENV_WRITER, |
|
64 RemoveRel => CMD_ENV_WRITER, |
|
65 RemoveSource => CMD_ENV_READER, |
|
66 PrepEnv => CMD_ENV_WRITER, |
|
67 PrepRel => CMD_ENV_WRITER, |
|
68 ValidateEnv => CMD_ENV_WRITER, |
|
69 ValidateRel => CMD_ENV_WRITER, |
|
70 EnvData => CMD_ENV_WRITER |
|
71 ); |
|
72 |
|
73 |
|
74 # |
|
75 # Public. |
|
76 # |
|
77 |
|
78 sub New { |
|
79 my $pkg = shift; |
|
80 my $self = {}; |
|
81 bless $self, $pkg; |
|
82 $self->{iniData} = shift; |
|
83 $self->{command} = shift; |
|
84 unless ($self->{iniData}->Win32ExtensionsDisabled()) { |
|
85 $self->OpenSemaphores(); |
|
86 unless ($self->CanRun()) { |
|
87 die "Error: Cannot run $self->{command} because another command is already running\n"; |
|
88 } |
|
89 } |
|
90 return $self; |
|
91 } |
|
92 |
|
93 |
|
94 # |
|
95 # Private. |
|
96 # |
|
97 |
|
98 sub CanRun { |
|
99 my $self = shift; |
|
100 $self->{canRun} = 0; |
|
101 my $commandType = $self->CommandType(); |
|
102 if ($commandType == CMD_INDEPENDANT) { |
|
103 $self->{canRun} = 1; |
|
104 } |
|
105 elsif ($commandType == CMD_ENV_READER) { |
|
106 unless ($self->WriterRunning()) { |
|
107 $self->{canRun} = 1; |
|
108 $self->IncReadersRunning(); |
|
109 } |
|
110 } |
|
111 elsif ($commandType == CMD_ENV_WRITER) { |
|
112 if (($self->NumReadersRunning() == 0) and not $self->WriterRunning()) { |
|
113 $self->{canRun} = 1; |
|
114 $self->SetWriterRunning(); |
|
115 } |
|
116 } |
|
117 return $self->{canRun}; |
|
118 } |
|
119 |
|
120 sub DESTROY { |
|
121 my $self = shift; |
|
122 if ($self->{canRun}) { |
|
123 my $commandType = $self->CommandType(); |
|
124 if ($commandType == CMD_INDEPENDANT) { |
|
125 # Nothing to do. |
|
126 } |
|
127 elsif ($commandType == CMD_ENV_READER) { |
|
128 $self->DecReadersRunning(); |
|
129 } |
|
130 elsif ($commandType == CMD_ENV_WRITER) { |
|
131 $self->ClearWriterRunning(); |
|
132 } |
|
133 } |
|
134 } |
|
135 |
|
136 sub OpenSemaphores { |
|
137 my $self = shift; |
|
138 my $currentEnvironment = Utils::CurrentDriveLetter() . lc(Utils::EpocRoot()); |
|
139 $currentEnvironment =~ s/[:\\\/]+/_/g; # Can't have slashes in semaphore name |
|
140 |
|
141 require Win32::Semaphore; |
|
142 # No longer 'use', as that fails with some versions of Perl |
|
143 $self->{writerSemaphore} = Win32::Semaphore->new(0, 2, WRITER_SEMAPHORE_NAME . $currentEnvironment) or die; # 2 because when counting the semaphore, it need to be incremented and then decremented (release(0, $var) doesn't work). |
|
144 $self->{readerSemaphore} = Win32::Semaphore->new(0, MAX_NUM_CONCURRENT_READERS, READER_SEMAPHORE_NAME . $currentEnvironment) or die; |
|
145 } |
|
146 |
|
147 sub CommandType { |
|
148 my $self = shift; |
|
149 die unless exists $commandInfo{$self->{command}}; |
|
150 return $commandInfo{$self->{command}}; |
|
151 } |
|
152 |
|
153 sub WriterRunning { |
|
154 my $self = shift; |
|
155 my $writerRunning = SemaphoreCount($self->{writerSemaphore}); |
|
156 die if $writerRunning > 1; |
|
157 return $writerRunning; |
|
158 } |
|
159 |
|
160 sub SetWriterRunning { |
|
161 my $self = shift; |
|
162 SemaphoreInc($self->{writerSemaphore}); |
|
163 } |
|
164 |
|
165 sub ClearWriterRunning { |
|
166 my $self = shift; |
|
167 SemaphoreDec($self->{writerSemaphore}); |
|
168 } |
|
169 |
|
170 sub NumReadersRunning { |
|
171 my $self = shift; |
|
172 return SemaphoreCount($self->{readerSemaphore}); |
|
173 } |
|
174 |
|
175 sub IncReadersRunning { |
|
176 my $self = shift; |
|
177 SemaphoreInc($self->{readerSemaphore}); |
|
178 } |
|
179 |
|
180 sub DecReadersRunning { |
|
181 my $self = shift; |
|
182 SemaphoreInc($self->{readerSemaphore}); |
|
183 } |
|
184 |
|
185 sub SemaphoreCount { |
|
186 my $semaphore = shift; |
|
187 my $count; |
|
188 $semaphore->release(1, $count) or die; |
|
189 $semaphore->wait(); |
|
190 return $count; |
|
191 } |
|
192 |
|
193 sub SemaphoreInc { |
|
194 my $semaphore = shift; |
|
195 $semaphore->release(1) or die; |
|
196 } |
|
197 |
|
198 sub SemaphoreDec { |
|
199 my $semaphore = shift; |
|
200 $semaphore->wait(); |
|
201 } |
|
202 |
|
203 1; |
|
204 |
|
205 =head1 NAME |
|
206 |
|
207 CommandController.pm - Provides a means of controlling which commands can run concurrently within a single environment. |
|
208 |
|
209 =head1 DESCRIPTION |
|
210 |
|
211 Certain commands can reliably be run while others are running, whereas others must be run in isolation. This class has responsibility for defining a set of rules regarding concurrent running of commands and ensuring that they are followed. Each command is classified into one of three types: |
|
212 |
|
213 =over 4 |
|
214 |
|
215 =item 1 Independant |
|
216 |
|
217 Commands of this type can be run regardless of whatever else may also be running at the time because they neither read nor modify the environment. Commands of this type: |
|
218 |
|
219 EnvMembership |
|
220 CleanRemote |
|
221 ExportEnv |
|
222 ExportRel |
|
223 ImportEnv |
|
224 ImportRel |
|
225 LatestVer |
|
226 PullEnv |
|
227 PullRel |
|
228 PushEnv |
|
229 PushRel |
|
230 DeltaEnv |
|
231 |
|
232 |
|
233 =item 2 Environment readers |
|
234 |
|
235 Commands of this type can be run provided there aren't any writers running. Commands of this type: |
|
236 |
|
237 BinInfo |
|
238 DiffEnv |
|
239 DiffRel |
|
240 MakeSnapShot |
|
241 ModNotes |
|
242 RemoveSource |
|
243 ViewNotes |
|
244 |
|
245 =item 3 Environment writers |
|
246 |
|
247 Commands of this type may modify the state of the environment, and so may only run providing there are no other writers or readers running. Commands of this type: |
|
248 |
|
249 CleanEnv |
|
250 EnvInfo |
|
251 GetEnv |
|
252 GetRel |
|
253 GetSource |
|
254 InstallSnapShot |
|
255 MakeEnv |
|
256 MakeRel |
|
257 RemoveRel |
|
258 PrepEnv |
|
259 PrepRel |
|
260 ValidateEnv |
|
261 ValidateRel |
|
262 |
|
263 =back |
|
264 |
|
265 To enforce these runs, multiple instances of C<CommandController> (running in different processes) need to know what else is running at any particular point in time. This information could have been stored in a file, but this has the significant problem that commands that are prematurely killed by the user (perhaps by hitting ctrl-c), they will not cleanup after themselves and so the environment could get stuck in an invalid state. To avoid this problem, a pair of Win32 semaphores are used to count the number of readers and writers currently active at any point in time. Note, only the counting properties of the semaphores are used, which is somewhat unusual (normally semaphores are used to control the execution of threads). The advantage of this scheme is that even if a command is prematurely killed by the user, its handles to the semaphoreswill be released. This may mean that for a period of time the semaphores may have invalid value, but once all commands that are currently running have completed, the semaphores will be destroyed (kernel side) and the environment is guaranteed of being in a 'ready to run' state. |
|
266 |
|
267 =head1 INTERFACE |
|
268 |
|
269 =head2 New |
|
270 |
|
271 Expects to be passed an C<IniData> reference and the name of the command that is about to be run (this is case sensitive). Creates and returns a new C<CommandController> instance if the command if free to run. Dies if not. The C<IniData> reference is used to determine if Win32 extensions have been disabled. If this is the case then the check to see if this command is free to run is not done (since doing so relies on Win32 functionality). |
|
272 |
|
273 =head1 KNOWN BUGS |
|
274 |
|
275 None. |
|
276 |
|
277 =head1 COPYRIGHT |
|
278 |
|
279 Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
280 All rights reserved. |
|
281 This component and the accompanying materials are made available |
|
282 under the terms of the License "Eclipse Public License v1.0" |
|
283 which accompanies this distribution, and is available |
|
284 at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
285 |
|
286 Initial Contributors: |
|
287 Nokia Corporation - initial contribution. |
|
288 |
|
289 Contributors: |
|
290 |
|
291 Description: |
|
292 |
|
293 |
|
294 =cut |