You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

146 lines
4.8 KiB

/*
* Copyright 2017 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "FIRMessagingDelayedMessageQueue.h"
#import "Protos/GtalkCore.pbobjc.h"
#import "FIRMessagingDefines.h"
#import "FIRMessagingRmqManager.h"
#import "FIRMessagingUtilities.h"
static const int kMaxQueuedMessageCount = 10;
@interface FIRMessagingDelayedMessageQueue ()
@property(nonatomic, readonly, weak) id<FIRMessagingRmqScanner> rmqScanner;
@property(nonatomic, readonly, copy) FIRMessagingSendDelayedMessagesHandler sendDelayedMessagesHandler;
@property(nonatomic, readwrite, assign) int persistedMessageCount;
// the scheduled timeout or -1 if not set
@property(nonatomic, readwrite, assign) int64_t scheduledTimeoutMilliseconds;
// The time of the last scan of the message DB,
// used to avoid retrieving messages more than once.
@property(nonatomic, readwrite, assign) int64_t lastDBScanTimestampSeconds;
@property(nonatomic, readwrite, strong) NSMutableArray *messages;
@property(nonatomic, readwrite, strong) NSTimer *sendTimer;
@end
@implementation FIRMessagingDelayedMessageQueue
- (instancetype)init {
FIRMessagingInvalidateInitializer();
}
- (instancetype)initWithRmqScanner:(id<FIRMessagingRmqScanner>)rmqScanner
sendDelayedMessagesHandler:(FIRMessagingSendDelayedMessagesHandler)sendDelayedMessagesHandler {
_FIRMessagingDevAssert(sendDelayedMessagesHandler, @"Invalid nil callback for delayed messages");
self = [super init];
if (self) {
_rmqScanner = rmqScanner;
_sendDelayedMessagesHandler = sendDelayedMessagesHandler;
_messages = [NSMutableArray arrayWithCapacity:10];
_scheduledTimeoutMilliseconds = -1;
}
return self;
}
- (BOOL)queueMessage:(GtalkDataMessageStanza *)message {
if (self.messages.count >= kMaxQueuedMessageCount) {
return NO;
}
if (message.ttl == 0) {
// ttl=0 messages aren't persisted, add it to memory
[self.messages addObject:message];
} else {
self.persistedMessageCount++;
}
int64_t timeoutMillis = [self calculateTimeoutInMillisWithDelayInSeconds:message.maxDelay];
if (![self isTimeoutScheduled] || timeoutMillis < self.scheduledTimeoutMilliseconds) {
[self scheduleTimeoutInMillis:timeoutMillis];
}
return YES;
}
- (NSArray *)removeDelayedMessages {
[self cancelTimeout];
if ([self messageCount] == 0) {
return @[];
}
NSMutableArray *delayedMessages = [NSMutableArray array];
// add the ttl=0 messages
if (self.messages.count) {
[delayedMessages addObjectsFromArray:delayedMessages];
[self.messages removeAllObjects];
}
// add persistent messages
if (self.persistedMessageCount > 0) {
FIRMessaging_WEAKIFY(self);
[self.rmqScanner scanWithRmqMessageHandler:nil
dataMessageHandler:^(int64_t rmqId, GtalkDataMessageStanza *stanza) {
FIRMessaging_STRONGIFY(self);
if ([stanza hasMaxDelay] &&
[stanza sent] >= self.lastDBScanTimestampSeconds) {
[delayedMessages addObject:stanza];
}
}];
self.lastDBScanTimestampSeconds = FIRMessagingCurrentTimestampInSeconds();
self.persistedMessageCount = 0;
}
return delayedMessages;
}
- (void)sendMessages {
if (self.sendDelayedMessagesHandler) {
self.sendDelayedMessagesHandler([self removeDelayedMessages]);
}
}
#pragma mark - Private
- (NSInteger)messageCount {
return self.messages.count + self.persistedMessageCount;
}
- (BOOL)isTimeoutScheduled {
return self.scheduledTimeoutMilliseconds > 0;
}
- (int64_t)calculateTimeoutInMillisWithDelayInSeconds:(int)delay {
return FIRMessagingCurrentTimestampInMilliseconds() + delay * 1000.0;
}
- (void)scheduleTimeoutInMillis:(int64_t)time {
[self cancelTimeout];
self.scheduledTimeoutMilliseconds = time;
double delay = (time - FIRMessagingCurrentTimestampInMilliseconds()) / 1000.0;
[self performSelector:@selector(sendMessages) withObject:self afterDelay:delay];
}
- (void)cancelTimeout {
if ([self isTimeoutScheduled]) {
[NSObject cancelPreviousPerformRequestsWithTarget:self
selector:@selector(sendMessages)
object:nil];
self.scheduledTimeoutMilliseconds = -1;
}
}
@end