PageRenderTime 62ms CodeModel.GetById 26ms app.highlight 32ms RepoModel.GetById 1ms app.codeStats 0ms

/core/externals/update-engine/Common/KSActionProcessor.m

http://macfuse.googlecode.com/
Objective C | 231 lines | 154 code | 47 blank | 30 comment | 26 complexity | dd2eb17ee9f71fb771a70f83d67954d7 MD5 | raw file
  1// Copyright 2008 Google Inc.
  2//
  3// Licensed under the Apache License, Version 2.0 (the "License");
  4// you may not use this file except in compliance with the License.
  5// You may obtain a copy of the License at
  6//
  7//     http://www.apache.org/licenses/LICENSE-2.0
  8//
  9// Unless required by applicable law or agreed to in writing, software
 10// distributed under the License is distributed on an "AS IS" BASIS,
 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12// See the License for the specific language governing permissions and
 13// limitations under the License.
 14
 15#import "KSActionProcessor.h"
 16#import "KSAction.h"
 17#import "GTMDefines.h"
 18#import "GTMLogger.h"
 19
 20@interface KSActionProcessor (PrivateMethods)
 21- (void)updateProgressWithFraction:(float)fraction;
 22- (void)setCurrentAction:(KSAction *)action;
 23- (void)processHead;
 24@end
 25
 26
 27@implementation KSActionProcessor
 28
 29- (id)init {
 30  return [self initWithDelegate:nil];
 31}
 32
 33- (id)initWithDelegate:(id)delegate {
 34  if ((self = [super init])) {
 35    delegate_ = delegate;
 36    actionQ_ = [[NSMutableArray alloc] init];
 37    _GTMDevAssert(actionQ_ != nil, @"actionQ_ should never be nil");
 38  }
 39  return self;
 40}
 41
 42- (void)dealloc {
 43  [self stopProcessing];  // This will release currentAction_
 44  [actionQ_ release];
 45  [super dealloc];
 46}
 47
 48- (id)delegate {
 49  return delegate_;
 50}
 51
 52- (void)setDelegate:(id)delegate {
 53  delegate_ = delegate;
 54}
 55
 56- (void)enqueueAction:(KSAction *)action {
 57  _GTMDevAssert(actionQ_ != nil, @"actionQ_ should never be nil");
 58  if (action == nil) return;
 59  @synchronized (self) {
 60    [actionQ_ addObject:action];
 61    [action setProcessor:self];
 62    if ([delegate_ respondsToSelector:@selector(processor:enqueuedAction:)])
 63      [delegate_ processor:self enqueuedAction:action];
 64  }
 65}
 66
 67- (NSArray *)actions {
 68  _GTMDevAssert(actionQ_ != nil, @"actionQ_ should never be nil");
 69  // We give the caller a non-mutable snapshot of the current actions so that
 70  // they can't touch our privates (ewww, that'd be bad).
 71  return [[actionQ_ copy] autorelease];
 72}
 73
 74- (void)startProcessing {
 75  @synchronized (self) {
 76    if (isProcessing_)
 77      return;
 78
 79    isProcessing_ = YES;
 80
 81    if ([delegate_ respondsToSelector:@selector(processingStarted:)])
 82      [delegate_ processingStarted:self];
 83
 84    [self processHead];
 85  }
 86}
 87
 88- (void)stopProcessing {
 89  @synchronized (self) {
 90    isProcessing_ = NO;
 91
 92    // Stop the current action then set it to nil (which will release it)
 93    [currentAction_ terminateAction];
 94    [currentAction_ setProcessor:nil];
 95    [self setCurrentAction:nil];
 96
 97    if ([delegate_ respondsToSelector:@selector(processingStopped:)])
 98      [delegate_ processingStopped:self];
 99  }
100}
101
102- (BOOL)isProcessing {
103  return isProcessing_;
104}
105
106- (float)progress {
107  return progress_;
108}
109
110- (KSAction *)currentAction {
111  return [[currentAction_ retain] autorelease];
112}
113
114- (int)actionsCompleted {
115  return actionsCompleted_;
116}
117
118- (NSString *)description {
119  return [NSString stringWithFormat:
120          @"<%@:%p isProcessing=%d actions=%d current=%@>", [self class],
121          self, isProcessing_, [actionQ_ count], [currentAction_ class]];
122}
123
124@end  // KSActionProcessor
125
126
127@implementation KSActionProcessor (KSActionProcessorCallbacks)
128
129- (void)runningAction:(KSAction *)action progress:(float)progress {
130  [self updateProgressWithFraction:progress];
131  SEL sel = @selector(processor:runningAction:progress:);
132  if ([delegate_ respondsToSelector:sel])
133    [delegate_ processor:self runningAction:action progress:progress];
134}
135
136- (void)finishedProcessing:(KSAction *)action successfully:(BOOL)wasOK {
137  @synchronized (self) {
138    if (action != currentAction_) {
139      // COV_NF_START
140      GTMLoggerError(@"finished processing %@, which was not the current action"
141                     @" (%@)", action, currentAction_);
142      return;
143      // COV_NF_END
144    }
145        
146    [self updateProgressWithFraction:1.0f];
147    
148    SEL sel = @selector(processor:finishedAction:successfully:);
149    if ([delegate_ respondsToSelector:sel])
150      [delegate_ processor:self finishedAction:action successfully:wasOK];
151
152    // Get rid of our current action since it's finished
153    [currentAction_ setProcessor:nil];
154    [self setCurrentAction:nil];
155    
156    actionsCompleted_++;
157
158    // If we're still supposed to be processing, process the next action
159    if (isProcessing_)
160      [self processHead];
161  }
162}
163
164@end  // KSActionProcessorCallbacks
165
166
167@implementation KSActionProcessor (PrivateMethods)
168
169- (void)updateProgressWithFraction:(float)fraction {
170  NSInteger totalActions = [actionQ_ count] + actionsCompleted_;
171  // Count the currently running action, and don't let totalActions == 0
172  if (isProcessing_ || totalActions == 0) totalActions += 1;
173  _GTMDevAssert(totalActions != 0, @"totalActions must not be 0");
174  float unit = (float)1 / totalActions;
175  @synchronized (self) {
176    progress_ = (unit * actionsCompleted_) + (unit * fraction);
177    // ensures 0.0 < progress_ < 1.0
178    progress_ = (progress_ > 1.0f) ? 1.0f : progress_;
179    progress_ = (progress_ < 0.0f) ? 0.0f : progress_;
180  }
181}
182
183- (void)setCurrentAction:(KSAction *)action {
184  [currentAction_ autorelease];
185  currentAction_ = [action retain];
186}
187
188- (void)processHead {
189  _GTMDevAssert(actionQ_ != nil, @"actionQ_ should never be nil");
190
191  @synchronized (self) {
192    _GTMDevAssert(currentAction_ == nil,
193                  @"currentAction_ (%@) must be nil before "
194                  @"processing a new action", currentAction_);
195
196    if ([actionQ_ count] > 0) {
197      // Get the first action and assign it to currentAction_, make sure the
198      // action is not already running, then remove it from the queue.
199      KSAction *action = [actionQ_ objectAtIndex:0];
200
201      // Make sure the action we're about to run isn't already running. This
202      // would be illegal, so we'll log and scream if it happens.
203      if ([action isRunning]) {
204        // COV_NF_START
205        [self stopProcessing];
206        _GTMDevAssert(NO, @"%@ can't run %@ because it's already running!",
207                      self, action);
208        return;
209        // COV_NF_END
210      }
211
212      [self setCurrentAction:action];
213      [actionQ_ removeObjectAtIndex:0];
214
215      if ([delegate_ respondsToSelector:@selector(processor:startingAction:)])
216        [delegate_ processor:self startingAction:action];
217
218      // Start the action
219      [action performAction];
220    } else {
221      isProcessing_ = NO;
222      // Tell the delegate that we're done processing
223      if ([delegate_ respondsToSelector:@selector(processingDone:)])
224        [delegate_ processingDone:self];
225
226      [self stopProcessing];
227    }
228  }
229}
230
231@end  // PrivateMethods