blob: 88aa7a7282fbf7b94eba5e46dc2a8bd7f11cbdaa [file] [log] [blame]
Jorge Canizales9409ad82015-02-18 16:19:56 -08001/*
2 *
Yang Gao5fc90292015-02-20 09:46:22 -08003 * Copyright 2015, Google Inc.
Jorge Canizales9409ad82015-02-18 16:19:56 -08004 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following disclaimer
14 * in the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Google Inc. nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 */
33
Jorge Canizales35f003b2015-07-17 21:14:36 -070034#import "GRXConcurrentWriteable.h"
Jorge Canizales5e0efd92015-02-17 18:23:58 -080035
Jorge Canizales3936ed72015-06-21 14:43:56 -070036#import <RxLibrary/GRXWriteable.h>
Jorge Canizales5e0efd92015-02-17 18:23:58 -080037
Jorge Canizales35f003b2015-07-17 21:14:36 -070038@interface GRXConcurrentWriteable ()
Jorge Canizales6531b2b2015-07-18 00:19:14 -070039// This is atomic so that cancellation can nillify it from any thread.
Jorge Canizales5e0efd92015-02-17 18:23:58 -080040@property(atomic, strong) id<GRXWriteable> writeable;
Jorge Canizales5e0efd92015-02-17 18:23:58 -080041@end
42
Jorge Canizales35f003b2015-07-17 21:14:36 -070043@implementation GRXConcurrentWriteable {
Jorge Canizales5e0efd92015-02-17 18:23:58 -080044 dispatch_queue_t _writeableQueue;
Jorge Canizalesb2c300c2015-05-18 17:19:16 -070045 // This ensures that writesFinishedWithError: is only sent once to the writeable.
Jorge Canizales5e0efd92015-02-17 18:23:58 -080046 dispatch_once_t _alreadyFinished;
47}
48
49- (instancetype)init {
Jorge Canizales6531b2b2015-07-18 00:19:14 -070050 return [self initWithWriteable:nil];
Jorge Canizales5e0efd92015-02-17 18:23:58 -080051}
52
53// Designated initializer
Muxi Yan895f3d82017-04-05 13:12:30 -070054- (instancetype)initWithWriteable:(id<GRXWriteable>)writeable
55 dispatchQueue:(dispatch_queue_t)queue {
Jorge Canizales5e0efd92015-02-17 18:23:58 -080056 if (self = [super init]) {
Muxi Yan895f3d82017-04-05 13:12:30 -070057 _writeableQueue = queue;
Jorge Canizales5e0efd92015-02-17 18:23:58 -080058 _writeable = writeable;
Jorge Canizales5e0efd92015-02-17 18:23:58 -080059 }
60 return self;
61}
62
Muxi Yan895f3d82017-04-05 13:12:30 -070063- (instancetype)initWithWriteable:(id<GRXWriteable>)writeable {
64 return [self initWithWriteable:writeable
65 dispatchQueue:dispatch_get_main_queue()];
66}
67
Jorge Canizales4c6f7782015-07-17 23:13:36 -070068- (void)enqueueValue:(id)value completionHandler:(void (^)())handler {
Jorge Canizales5e0efd92015-02-17 18:23:58 -080069 dispatch_async(_writeableQueue, ^{
Jorge Canizales6531b2b2015-07-18 00:19:14 -070070 // We're racing a possible cancellation performed by another thread. To turn all already-
71 // enqueued messages into noops, cancellation nillifies the writeable property. If we get it
72 // before it's nil, we won the race.
Jorge Canizales5e0efd92015-02-17 18:23:58 -080073 id<GRXWriteable> writeable = self.writeable;
74 if (writeable) {
Jorge Canizales4c6f7782015-07-17 23:13:36 -070075 [writeable writeValue:value];
Jorge Canizales5e0efd92015-02-17 18:23:58 -080076 handler();
77 }
78 });
79}
80
81- (void)enqueueSuccessfulCompletion {
82 dispatch_async(_writeableQueue, ^{
83 dispatch_once(&_alreadyFinished, ^{
Jorge Canizales6531b2b2015-07-18 00:19:14 -070084 // Cancellation is now impossible. None of the other three blocks can run concurrently with
85 // this one.
Jorge Canizalesb2c300c2015-05-18 17:19:16 -070086 [self.writeable writesFinishedWithError:nil];
Jorge Canizales6531b2b2015-07-18 00:19:14 -070087 // Skip any possible message to the wrapped writeable enqueued after this one.
Jorge Canizales5e0efd92015-02-17 18:23:58 -080088 self.writeable = nil;
Jorge Canizales5e0efd92015-02-17 18:23:58 -080089 });
90 });
91}
92
93- (void)cancelWithError:(NSError *)error {
94 NSAssert(error, @"For a successful completion, use enqueueSuccessfulCompletion.");
95 dispatch_once(&_alreadyFinished, ^{
Jorge Canizales6531b2b2015-07-18 00:19:14 -070096 // Skip any of the still-enqueued messages to the wrapped writeable. We use the atomic setter to
97 // nillify writeable because we might be running concurrently with the blocks in
98 // _writeableQueue, and assignment with ARC isn't atomic.
Jorge Canizales5e0efd92015-02-17 18:23:58 -080099 id<GRXWriteable> writeable = self.writeable;
100 self.writeable = nil;
101
102 dispatch_async(_writeableQueue, ^{
Jorge Canizalesb2c300c2015-05-18 17:19:16 -0700103 [writeable writesFinishedWithError:error];
Jorge Canizales5e0efd92015-02-17 18:23:58 -0800104 });
105 });
106}
107
108- (void)cancelSilently {
109 dispatch_once(&_alreadyFinished, ^{
Jorge Canizales6531b2b2015-07-18 00:19:14 -0700110 // Skip any of the still-enqueued messages to the wrapped writeable. We use the atomic setter to
111 // nillify writeable because we might be running concurrently with the blocks in
112 // _writeableQueue, and assignment with ARC isn't atomic.
Jorge Canizales5e0efd92015-02-17 18:23:58 -0800113 self.writeable = nil;
Jorge Canizales5e0efd92015-02-17 18:23:58 -0800114 });
115}
116@end