OLD | NEW |
1 // Copyright 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #import "ios/chrome/app/deferred_initialization_runner.h" | 5 #import "ios/chrome/app/deferred_initialization_runner.h" |
6 | 6 |
7 #include <stdint.h> | 7 #include <stdint.h> |
8 | 8 |
| 9 #import "base/ios/weak_nsobject.h" |
9 #include "base/logging.h" | 10 #include "base/logging.h" |
| 11 #include "base/mac/scoped_block.h" |
10 #include "base/mac/scoped_nsobject.h" | 12 #include "base/mac/scoped_nsobject.h" |
11 | 13 |
12 // An object encapsulating the deferred execution of a block of initialization | 14 // An object encapsulating the deferred execution of a block of initialization |
13 // code. | 15 // code. |
14 @interface DeferredInitializationBlock : NSObject { | 16 @interface DeferredInitializationBlock : NSObject { |
15 // A string to reference the initialization block. | 17 // A string to reference the initialization block. |
16 base::scoped_nsobject<NSString> _name; | 18 base::scoped_nsobject<NSString> _name; |
17 // A block of code to execute. | 19 // A block of code to execute. |
18 base::scoped_nsprotocol<ProceduralBlock> _runBlock; | 20 base::mac::ScopedBlock<ProceduralBlock> _runBlock; |
19 } | 21 } |
20 | 22 |
| 23 - (instancetype)init NS_UNAVAILABLE; |
| 24 |
21 // Designated initializer. | 25 // Designated initializer. |
22 - (id)initWithName:(NSString*)name block:(ProceduralBlock)block; | 26 - (instancetype)initWithName:(NSString*)name |
| 27 block:(ProceduralBlock)block NS_DESIGNATED_INITIALIZER; |
23 | 28 |
24 // Dispatches the deferred execution the block after |delaySeconds|. | 29 // Dispatches the deferred execution the block after |delaySeconds|. |
| 30 // Deprecated. |
25 - (void)dispatch:(NSTimeInterval)delaySeconds; | 31 - (void)dispatch:(NSTimeInterval)delaySeconds; |
26 | 32 |
27 // Executes the deferred block now. | 33 // Executes the deferred block now. |
28 - (void)runSynchronously; | 34 - (void)run; |
29 | 35 |
30 // Cancels the block's execution. | 36 // Cancels the block's execution. |
31 - (void)cancel; | 37 - (void)cancel; |
| 38 |
32 @end | 39 @end |
33 | 40 |
34 @implementation DeferredInitializationBlock | 41 @implementation DeferredInitializationBlock |
35 | 42 |
36 // Overrides default designated initializer. | 43 // Overrides default designated initializer. |
37 - (id)init { | 44 - (instancetype)init { |
38 NOTREACHED(); | 45 NOTREACHED(); |
39 return nil; | 46 return nil; |
40 } | 47 } |
41 | 48 |
42 - (id)initWithName:(NSString*)name block:(ProceduralBlock)block { | |
43 DCHECK(block); | |
44 self = [super init]; | |
45 if (self) { | |
46 _name.reset([name copy]); | |
47 _runBlock.reset([block copy]); | |
48 } | |
49 return self; | |
50 } | |
51 | |
52 - (void)dispatch:(NSTimeInterval)delaySeconds { | 49 - (void)dispatch:(NSTimeInterval)delaySeconds { |
53 int64_t nanoseconds = delaySeconds * NSEC_PER_SEC; | 50 int64_t nanoseconds = delaySeconds * NSEC_PER_SEC; |
54 DCHECK([NSThread isMainThread]); | 51 DCHECK([NSThread isMainThread]); |
55 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, nanoseconds), | 52 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, nanoseconds), |
56 dispatch_get_main_queue(), ^() { | 53 dispatch_get_main_queue(), ^() { |
57 [self runSynchronously]; | 54 [self run]; |
58 }); | 55 }); |
59 } | 56 } |
60 | 57 |
61 - (void)runSynchronously { | 58 - (instancetype)initWithName:(NSString*)name block:(ProceduralBlock)block { |
| 59 DCHECK(block); |
| 60 self = [super init]; |
| 61 if (self) { |
| 62 _name.reset([name copy]); |
| 63 _runBlock.reset(block, base::scoped_policy::RETAIN); |
| 64 } |
| 65 return self; |
| 66 } |
| 67 |
| 68 - (void)run { |
| 69 DCHECK([NSThread isMainThread]); |
62 ProceduralBlock deferredBlock = _runBlock.get(); | 70 ProceduralBlock deferredBlock = _runBlock.get(); |
63 if (!deferredBlock) | 71 if (!deferredBlock) |
64 return; | 72 return; |
65 deferredBlock(); | 73 deferredBlock(); |
66 [[DeferredInitializationRunner sharedInstance] cancelBlockNamed:_name]; | 74 [[DeferredInitializationRunner sharedInstance] cancelBlockNamed:_name]; |
67 } | 75 } |
68 | 76 |
69 - (void)cancel { | 77 - (void)cancel { |
70 _runBlock.reset(); | 78 _runBlock.reset(); |
71 } | 79 } |
72 | 80 |
73 @end | 81 @end |
74 | 82 |
75 @implementation DeferredInitializationRunner { | 83 @interface DeferredInitializationRunner () { |
| 84 base::scoped_nsobject<NSMutableArray> _blocksNameQueue; |
76 base::scoped_nsobject<NSMutableDictionary> _runBlocks; | 85 base::scoped_nsobject<NSMutableDictionary> _runBlocks; |
| 86 BOOL _isBlockScheduled; |
77 } | 87 } |
78 | 88 |
| 89 // Schedule the next block to be run after |delay| it will automatically |
| 90 // schedule the next block after |delayBetweenBlocks|. |
| 91 - (void)scheduleNextBlockWithDelay:(NSTimeInterval)delay; |
| 92 |
| 93 // Time interval between two blocks. Default value is 200ms. |
| 94 @property(nonatomic) NSTimeInterval delayBetweenBlocks; |
| 95 |
| 96 // Time interval before running the first block. Default value is 3s. |
| 97 @property(nonatomic) NSTimeInterval delayBeforeFirstBlock; |
| 98 |
| 99 @end |
| 100 |
| 101 @implementation DeferredInitializationRunner |
| 102 |
| 103 @synthesize delayBetweenBlocks = _delayBetweenBlocks; |
| 104 @synthesize delayBeforeFirstBlock = _delayBeforeFirstBlock; |
| 105 |
79 + (DeferredInitializationRunner*)sharedInstance { | 106 + (DeferredInitializationRunner*)sharedInstance { |
80 static DeferredInitializationRunner* instance = | 107 static dispatch_once_t once = 0; |
81 [[DeferredInitializationRunner alloc] init]; | 108 static DeferredInitializationRunner* instance = nil; |
| 109 dispatch_once(&once, ^{ |
| 110 instance = [[DeferredInitializationRunner alloc] init]; |
| 111 }); |
82 return instance; | 112 return instance; |
83 } | 113 } |
84 | 114 |
85 - (id)init { | 115 - (id)init { |
86 self = [super init]; | 116 self = [super init]; |
87 if (self) | 117 if (self) { |
| 118 _blocksNameQueue.reset([[NSMutableArray array] retain]); |
88 _runBlocks.reset([[NSMutableDictionary dictionary] retain]); | 119 _runBlocks.reset([[NSMutableDictionary dictionary] retain]); |
| 120 _isBlockScheduled = NO; |
| 121 _delayBetweenBlocks = 0.2; |
| 122 _delayBeforeFirstBlock = 3.0; |
| 123 } |
89 return self; | 124 return self; |
90 } | 125 } |
91 | 126 |
| 127 - (void)enqueueBlockNamed:(NSString*)name block:(ProceduralBlock)block { |
| 128 DCHECK(name); |
| 129 DCHECK([NSThread isMainThread]); |
| 130 [self cancelBlockNamed:name]; |
| 131 [_blocksNameQueue addObject:name]; |
| 132 |
| 133 base::scoped_nsobject<DeferredInitializationBlock> deferredBlock( |
| 134 [[DeferredInitializationBlock alloc] initWithName:name block:block]); |
| 135 [_runBlocks setObject:deferredBlock forKey:name]; |
| 136 |
| 137 if (!_isBlockScheduled) { |
| 138 [self scheduleNextBlockWithDelay:self.delayBeforeFirstBlock]; |
| 139 } |
| 140 } |
| 141 |
| 142 - (void)scheduleNextBlockWithDelay:(NSTimeInterval)delay { |
| 143 DCHECK([NSThread isMainThread]); |
| 144 _isBlockScheduled = NO; |
| 145 NSString* nextBlockName = [_blocksNameQueue firstObject]; |
| 146 if (!nextBlockName) |
| 147 return; |
| 148 |
| 149 DeferredInitializationBlock* nextBlock = |
| 150 [_runBlocks objectForKey:nextBlockName]; |
| 151 DCHECK(nextBlock); |
| 152 |
| 153 base::WeakNSObject<DeferredInitializationRunner> weakSelf(self); |
| 154 |
| 155 dispatch_after( |
| 156 dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), |
| 157 dispatch_get_main_queue(), ^{ |
| 158 [nextBlock run]; |
| 159 [weakSelf scheduleNextBlockWithDelay:[weakSelf delayBetweenBlocks]]; |
| 160 }); |
| 161 |
| 162 _isBlockScheduled = YES; |
| 163 [_blocksNameQueue removeObjectAtIndex:0]; |
| 164 } |
| 165 |
92 - (void)runBlockNamed:(NSString*)name | 166 - (void)runBlockNamed:(NSString*)name |
93 after:(NSTimeInterval)delaySeconds | 167 after:(NSTimeInterval)delaySeconds |
94 block:(ProceduralBlock)block { | 168 block:(ProceduralBlock)block { |
95 DCHECK(name); | 169 DCHECK(name); |
96 // Safety check in case this function is called with a nanosecond or | 170 // Safety check in case this function is called with a nanosecond or |
97 // microsecond parameter by mistake. | 171 // microsecond parameter by mistake. |
98 DCHECK(delaySeconds < 3600.0); | 172 DCHECK(delaySeconds < 3600.0); |
99 // Cancels the previously scheduled block, if there is one, so this | 173 // Cancels the previously scheduled block, if there is one, so this |
100 // |name| block will not be run more than once. | 174 // |name| block will not be run more than once. |
101 [[_runBlocks objectForKey:name] cancel]; | 175 [[_runBlocks objectForKey:name] cancel]; |
102 base::scoped_nsobject<DeferredInitializationBlock> deferredBlock( | 176 base::scoped_nsobject<DeferredInitializationBlock> deferredBlock( |
103 [[DeferredInitializationBlock alloc] initWithName:name block:block]); | 177 [[DeferredInitializationBlock alloc] initWithName:name block:block]); |
104 [_runBlocks setObject:deferredBlock forKey:name]; | 178 [_runBlocks setObject:deferredBlock forKey:name]; |
105 [deferredBlock dispatch:delaySeconds]; | 179 [deferredBlock dispatch:delaySeconds]; |
106 } | 180 } |
107 | 181 |
108 - (void)runBlockIfNecessary:(NSString*)name { | 182 - (void)runBlockIfNecessary:(NSString*)name { |
109 [[_runBlocks objectForKey:name] runSynchronously]; | 183 DCHECK([NSThread isMainThread]); |
| 184 [[_runBlocks objectForKey:name] run]; |
110 } | 185 } |
111 | 186 |
112 - (void)cancelBlockNamed:(NSString*)name { | 187 - (void)cancelBlockNamed:(NSString*)name { |
| 188 DCHECK([NSThread isMainThread]); |
113 DCHECK(name); | 189 DCHECK(name); |
| 190 [_blocksNameQueue removeObject:name]; |
114 [[_runBlocks objectForKey:name] cancel]; | 191 [[_runBlocks objectForKey:name] cancel]; |
115 [_runBlocks removeObjectForKey:name]; | 192 [_runBlocks removeObjectForKey:name]; |
116 } | 193 } |
117 | 194 |
118 - (NSUInteger)numberOfBlocksRemaining { | 195 - (NSUInteger)numberOfBlocksRemaining { |
119 return [_runBlocks count]; | 196 return [_runBlocks count]; |
120 } | 197 } |
121 | 198 |
122 @end | 199 @end |
OLD | NEW |