FSTBufferedWriter.m 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. /*
  2. * Copyright 2017 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import <Protobuf/GPBProtocolBuffers.h>
  17. #import "Firestore/Source/Remote/FSTBufferedWriter.h"
  18. NS_ASSUME_NONNULL_BEGIN
  19. @implementation FSTBufferedWriter {
  20. GRXWriterState _state;
  21. NSMutableArray<NSData *> *_queue;
  22. id<GRXWriteable> _writeable;
  23. }
  24. - (instancetype)init {
  25. if (self = [super init]) {
  26. _state = GRXWriterStateNotStarted;
  27. _queue = [[NSMutableArray alloc] init];
  28. }
  29. return self;
  30. }
  31. #pragma mark - GRXWriteable implementation
  32. /** Push the next value of the sequence to the receiving object. */
  33. - (void)writeValue:(id)value {
  34. if (_state == GRXWriterStateStarted && _queue.count == 0) {
  35. // Skip the queue.
  36. [_writeable writeValue:value];
  37. } else {
  38. // Buffer the new value. Note that the value is assumed to be transient and doesn't need to
  39. // be copied.
  40. [_queue addObject:value];
  41. }
  42. }
  43. /**
  44. * Signal that the sequence is completed, or that an error ocurred. After this message is sent to
  45. * the receiver, neither it nor writeValue: may be called again.
  46. */
  47. - (void)writesFinishedWithError:(nullable NSError *)error {
  48. // Unimplemented. If we ever wanted to implement sender-side initiated half close we could do so
  49. // by buffering (or sending) and error.
  50. [self doesNotRecognizeSelector:_cmd];
  51. }
  52. #pragma mark GRXWriter implementation
  53. // The GRXWriter implementation defines the send side of the RPC stream. Once the RPC is ready it
  54. // will call startWithWriteable passing a GRXWriteable into which requests can be written but only
  55. // when the GRXWriter is in the started state.
  56. /**
  57. * Called by GRPCCall when it is ready to accept for the first request. Requests should be written
  58. * to the passed writeable.
  59. *
  60. * GRPCCall will synchronize on the receiver around this call.
  61. */
  62. - (void)startWithWriteable:(id<GRXWriteable>)writeable {
  63. _state = GRXWriterStateStarted;
  64. _writeable = writeable;
  65. }
  66. /**
  67. * Called by GRPCCall to implement flow control on the sending side of the stream. After each
  68. * writeValue: on the requestsWriteable, GRPCCall will call setState:GRXWriterStatePaused to apply
  69. * backpressure. Once the stream is ready to accept another message, GRPCCall will call
  70. * setState:GRXWriterStateStarted.
  71. *
  72. * GRPCCall will synchronize on the receiver around this call.
  73. */
  74. - (void)setState:(GRXWriterState)newState {
  75. // Manual transitions are only allowed from the started or paused states.
  76. if (_state == GRXWriterStateNotStarted || _state == GRXWriterStateFinished) {
  77. return;
  78. }
  79. switch (newState) {
  80. case GRXWriterStateFinished:
  81. _state = newState;
  82. // Per GRXWriter's contract, setting the state to Finished manually means one doesn't wish the
  83. // writeable to be messaged anymore.
  84. _queue = nil;
  85. _writeable = nil;
  86. return;
  87. case GRXWriterStatePaused:
  88. _state = newState;
  89. return;
  90. case GRXWriterStateStarted:
  91. if (_state == GRXWriterStatePaused) {
  92. _state = newState;
  93. [self writeBufferedMessages];
  94. }
  95. return;
  96. case GRXWriterStateNotStarted:
  97. return;
  98. }
  99. }
  100. - (void)finishWithError:(nullable NSError *)error {
  101. [_writeable writesFinishedWithError:error];
  102. self.state = GRXWriterStateFinished;
  103. }
  104. - (void)writeBufferedMessages {
  105. while (_state == GRXWriterStateStarted && _queue.count > 0) {
  106. id value = _queue[0];
  107. [_queue removeObjectAtIndex:0];
  108. // In addition to writing the value here GRPC will apply backpressure by pausing the GRXWriter
  109. // wrapping this buffer. That writer must call -pauseMessages which will cause this loop to
  110. // exit. Synchronization is not required since the callback happens within the body of the
  111. // writeValue implementation.
  112. [_writeable writeValue:value];
  113. }
  114. }
  115. @end
  116. NS_ASSUME_NONNULL_END