releasing/cbrtools/perl/CommandController.pm
changeset 602 3145852acc89
equal deleted inserted replaced
600:6d08f4a05d93 602:3145852acc89
       
     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