Skip to content

Commit

Permalink
Update RTP5 for 0.9 (#570)
Browse files Browse the repository at this point in the history
* Update RTP5

* Update RTP5a

* Update RTP5b

* RTP5c

* Test suite: replaceAcksWithNacks

 - better code completion

* RTP5f

* PresenceMap: existing members before Sync

* Fix: presence get members should not wait for sync if sync is not in progress

* PresenceMap: reenter local member

* Test suite: replacing acks with nacks even with Presence action

* Fix: should queue messages before the attach operation

* Test suite: ARTPresenceAction description

* Better debug info

* Fix: should continue incrementing msgSerial serially if the connection resumes successfully

* Remove warnings

* Test suite: timings
  • Loading branch information
ricardopereira authored Feb 6, 2017
1 parent d9e828c commit 18e43ea
Show file tree
Hide file tree
Showing 21 changed files with 945 additions and 113 deletions.
4 changes: 4 additions & 0 deletions Ably.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@
D7F1D3771BF4DE72001A4B5E /* ARTRealtimePresence.h in Headers */ = {isa = PBXBuildFile; fileRef = D7F1D3751BF4DE72001A4B5E /* ARTRealtimePresence.h */; settings = {ATTRIBUTES = (Public, ); }; };
D7F1D3781BF4DE72001A4B5E /* ARTRealtimePresence.m in Sources */ = {isa = PBXBuildFile; fileRef = D7F1D3761BF4DE72001A4B5E /* ARTRealtimePresence.m */; };
D7F1D37A1BF4E33A001A4B5E /* ARTRestChannel+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = D7F1D3791BF4E33A001A4B5E /* ARTRestChannel+Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
D7F2B8B21E42410D00B65151 /* ARTPresenceMessage+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = D7F2B8B11E42410D00B65151 /* ARTPresenceMessage+Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
EB0505FC1C5BD7C4006BA7E2 /* ARTBaseMessage+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = EB0505FB1C5BD7C4006BA7E2 /* ARTBaseMessage+Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
EB1AE0CC1C5C1EB200D62250 /* ARTEventEmitter+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = EB1AE0CB1C5C1EB200D62250 /* ARTEventEmitter+Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
EB1AE0CE1C5C3A4900D62250 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB1AE0CD1C5C3A4900D62250 /* Utilities.swift */; };
Expand Down Expand Up @@ -399,6 +400,7 @@
D7F1D3751BF4DE72001A4B5E /* ARTRealtimePresence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTRealtimePresence.h; sourceTree = "<group>"; };
D7F1D3761BF4DE72001A4B5E /* ARTRealtimePresence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTRealtimePresence.m; sourceTree = "<group>"; };
D7F1D3791BF4E33A001A4B5E /* ARTRestChannel+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ARTRestChannel+Private.h"; sourceTree = "<group>"; };
D7F2B8B11E42410D00B65151 /* ARTPresenceMessage+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ARTPresenceMessage+Private.h"; sourceTree = "<group>"; };
E3ECA6832E9694DC9EFC5DDD /* Pods-AblySpec.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AblySpec.debug.xcconfig"; path = "Pods/Target Support Files/Pods-AblySpec/Pods-AblySpec.debug.xcconfig"; sourceTree = "<group>"; };
EB0505FB1C5BD7C4006BA7E2 /* ARTBaseMessage+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ARTBaseMessage+Private.h"; sourceTree = "<group>"; };
EB1AE0CB1C5C1EB200D62250 /* ARTEventEmitter+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ARTEventEmitter+Private.h"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -708,6 +710,7 @@
D746AE261BBB61C9003ECEF8 /* ARTPresence.h */,
D746AE271BBB61C9003ECEF8 /* ARTPresence.m */,
96A5079F1A377AA50077CDF8 /* ARTPresenceMessage.h */,
D7F2B8B11E42410D00B65151 /* ARTPresenceMessage+Private.h */,
96A507A01A377AA50077CDF8 /* ARTPresenceMessage.m */,
1C2B0FFB1B136A6D00E3633C /* ARTPresenceMap.h */,
1C2B0FFC1B136A6D00E3633C /* ARTPresenceMap.m */,
Expand Down Expand Up @@ -858,6 +861,7 @@
D746AE251BBB611C003ECEF8 /* ARTChannel+Private.h in Headers */,
D7F1D3731BF4DE07001A4B5E /* ARTRestPresence.h in Headers */,
D7B17EE31C07208B00A6958E /* ARTConnectionDetails.h in Headers */,
D7F2B8B21E42410D00B65151 /* ARTPresenceMessage+Private.h in Headers */,
EBFA366E1D58B05000B09AA7 /* ARTRestPresence+Private.h in Headers */,
EB2D85011CD769C800F23CDA /* ARTOSReachability.h in Headers */,
960D07971A46FFC300ED8C8C /* ARTRest+Private.h in Headers */,
Expand Down
2 changes: 1 addition & 1 deletion Source/ARTBaseMessage.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ ART_ASSUME_NONNULL_BEGIN
@interface ARTBaseMessage : NSObject<NSCopying>

/// A unique id for this message
@property (strong, nonatomic) NSString *id;
@property (nullable, strong, nonatomic) NSString *id;

/// The timestamp for this message
@property (strong, nonatomic, art_nullable) NSDate *timestamp;
Expand Down
4 changes: 2 additions & 2 deletions Source/ARTJsonLikeEncoder.m
Original file line number Diff line number Diff line change
Expand Up @@ -696,7 +696,7 @@ - (void)writeData:(id)data encoding:(NSString *)encoding toDictionary:(NSMutable

- (id)decode:(NSData *)data {
id decoded = [_delegate decode:data];
[_logger verbose:@"RS:%p ARTJsonLikeEncoder<%@> decoding '%@'; got: %@", _rest, [_delegate formatAsString], data, decoded];
[_logger debug:@"RS:%p ARTJsonLikeEncoder<%@> decoding '%@'; got: %@", _rest, [_delegate formatAsString], data, decoded];
return decoded;
}

Expand All @@ -718,7 +718,7 @@ - (NSArray *)decodeArray:(NSData *)data {

- (NSData *)encode:(id)obj {
NSData *encoded = [_delegate encode:obj];
[_logger verbose:@"RS:%p ARTJsonLikeEncoder<%@> encoding '%@'; got: %@", _rest, [_delegate formatAsString], obj, encoded];
[_logger debug:@"RS:%p ARTJsonLikeEncoder<%@> encoding '%@'; got: %@", _rest, [_delegate formatAsString], obj, encoded];
return encoded;
}

Expand Down
1 change: 1 addition & 0 deletions Source/ARTLog.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ typedef NS_ENUM(NSUInteger, ARTLogLevel) {
@interface ARTLog (Shorthand)

- (void)verbose:(NSString *)format, ... NS_FORMAT_FUNCTION(1,2);
- (void)verbose:(const char *)fileName line:(NSUInteger)line message:(NSString *)message, ... NS_FORMAT_FUNCTION(3,4);
- (void)debug:(NSString *)format, ... NS_FORMAT_FUNCTION(1,2);
- (void)debug:(const char *)fileName line:(NSUInteger)line message:(NSString *)message, ... NS_FORMAT_FUNCTION(3,4);
- (void)info:(NSString *)format, ... NS_FORMAT_FUNCTION(1,2);
Expand Down
8 changes: 8 additions & 0 deletions Source/ARTLog.m
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,14 @@ - (void)verbose:(NSString *)format, ... {
va_end(args);
}


- (void)verbose:(const char *)fileName line:(NSUInteger)line message:(NSString *)message, ... {
va_list args;
va_start(args, message);
[self log:[[NSString alloc] initWithFormat:[NSString stringWithFormat:@"(%@:%lu) %@", [[NSString stringWithUTF8String:fileName] lastPathComponent], (unsigned long)line, message] arguments:args] level:ARTLogLevelVerbose];
va_end(args);
}

- (void)debug:(NSString *)format, ... {
va_list args;
va_start(args, format);
Expand Down
21 changes: 16 additions & 5 deletions Source/ARTPresenceMap.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,44 @@
#import <Foundation/Foundation.h>
#import "CompatibilityMacros.h"

@class ARTPresenceMap;
@class ARTPresenceMessage;
@class ARTErrorInfo;
@class ARTLog;

ART_ASSUME_NONNULL_BEGIN

/// ARTPresenceMapDelegate
@protocol ARTPresenceMapDelegate <NSObject>
@property (nonatomic, readonly) NSString *connectionId;
- (void)map:(ARTPresenceMap *)map didRemovedMemberNoLongerPresent:(ARTPresenceMessage *)presence;
- (void)map:(ARTPresenceMap *)map shouldReenterLocalMember:(ARTPresenceMessage *)presence;
@end

/// Used to maintain a list of members present on a channel
@interface ARTPresenceMap : NSObject

/// List of members.
/// The key is the clientId and the value is the latest relevant ARTPresenceMessage for that clientId.
@property (readonly, atomic, getter=getMembers) __GENERIC(NSDictionary, NSString *, ARTPresenceMessage *) *members;
@property (readonly, atomic) NSDictionary<NSString *, ARTPresenceMessage *> *members;

/// List of internal members.
/// The key is the clientId and the value is the latest relevant ARTPresenceMessage for that clientId.
@property (readonly, atomic) NSDictionary<NSString *, ARTPresenceMessage *> *localMembers;
@property (readonly, atomic) NSMutableSet<ARTPresenceMessage *> *localMembers;

@property (nullable, weak) id<ARTPresenceMapDelegate> delegate;

@property (readwrite, nonatomic, assign) int64_t syncMsgSerial;
@property (readwrite, nonatomic, nullable) NSString *syncChannelSerial;
@property (readonly, nonatomic, assign) BOOL syncComplete;
@property (readonly, nonatomic, getter=getSyncInProgress) BOOL syncInProgress;
@property (readonly, nonatomic, assign) NSUInteger syncSessionId;
@property (readonly, nonatomic, getter=syncComplete) BOOL syncComplete;
@property (readonly, nonatomic, getter=syncInProgress) BOOL syncInProgress;

- (instancetype)init UNAVAILABLE_ATTRIBUTE;
- (instancetype)initWithLogger:(ARTLog *)logger;

- (BOOL)add:(ARTPresenceMessage *)message;
- (void)clean;
- (void)reset;

- (void)startSync;
- (void)endSync;
Expand Down
129 changes: 98 additions & 31 deletions Source/ARTPresenceMap.m
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,24 @@

#import "ARTPresenceMap.h"
#import "ARTPresenceMessage.h"
#import "ARTPresenceMessage+Private.h"
#import "ARTEventEmitter.h"
#import "ARTLog.h"

typedef NS_ENUM(NSUInteger, ARTPresenceSyncState) {
ARTPresenceSyncInitialized,
ARTPresenceSyncStarted, //ItemType: nil
ARTPresenceSyncEnded, //ItemType: NSArray<ARTPresenceMessage *>*
ARTPresenceSyncFailed //ItemType: ARTErrorInfo*
};

@interface ARTPresenceMap () {
BOOL _syncStarted;
ARTPresenceSyncState _syncState;
ARTEventEmitter<NSNumber * /*ARTSyncState*/, id> *_syncEventEmitter;
NSMutableDictionary<NSString *, ARTPresenceMessage *> *_members;
NSMutableSet<ARTPresenceMessage *> *_localMembers;
}

@property (readwrite, strong, atomic) __GENERIC(NSMutableDictionary, NSString *, ARTPresenceMessage *) *recentMembers;

@end

@implementation ARTPresenceMap {
Expand All @@ -34,41 +36,76 @@ - (instancetype)initWithLogger:(ARTLog *)logger {
self = [super init];
if(self) {
_logger = logger;
_recentMembers = [NSMutableDictionary dictionary];
_syncStarted = false;
_syncComplete = false;
[self reset];
_syncSessionId = 0;
_syncState = ARTPresenceSyncInitialized;
_syncEventEmitter = [[ARTEventEmitter alloc] init];
}
return self;
}

- (__GENERIC(NSDictionary, NSString *, ARTPresenceMessage *) *)getMembers {
return self.recentMembers;
- (NSDictionary<NSString *, ARTPresenceMessage *> *)members {
return _members;
}

- (NSMutableSet<ARTPresenceMessage *> *)localMembers {
return _localMembers;
}

- (BOOL)add:(ARTPresenceMessage *)message {
ARTPresenceMessage *latest = [self.recentMembers objectForKey:message.clientId];
ARTPresenceMessage *latest = [_members objectForKey:message.clientId];
if ([self isNewestPresence:message comparingWith:latest]) {
ARTPresenceMessage *messageCopy = [message copy];
switch (message.action) {
case ARTPresenceEnter:
case ARTPresenceUpdate:
messageCopy.action = ARTPresencePresent;
// intentional fallthrough
case ARTPresencePresent:
[self internalAdd:messageCopy];
break;
case ARTPresenceLeave:
if (self.syncInProgress) {
messageCopy.action = ARTPresenceAbsent;
}
[self internalRemove:messageCopy];
break;
default:
break;
}
[self.recentMembers setObject:messageCopy forKey:message.clientId];
return YES;
}
latest.syncSessionId = _syncSessionId;
return NO;
}

- (void)internalAdd:(ARTPresenceMessage *)message {
[self internalAdd:message withSessionId:_syncSessionId];
}

- (void)internalAdd:(ARTPresenceMessage *)message withSessionId:(NSUInteger)sessionId {
message.syncSessionId = sessionId;
[_members setObject:message forKey:message.clientId];
// Local member
if ([message.connectionId isEqualToString:[self.delegate connectionId]]) {
[_localMembers addObject:message];
[_logger debug:__FILE__ line:__LINE__ message:@"local member %@ added", message.memberKey];
}
}

- (void)internalRemove:(ARTPresenceMessage *)message {
[self internalRemove:message force:false];
}

- (void)internalRemove:(ARTPresenceMessage *)message force:(BOOL)force {
if (!force && self.syncInProgress) {
message.action = ARTPresenceAbsent;
// Should be removed after Sync ends
[self internalAdd:message withSessionId:message.syncSessionId];
}
else {
[_members removeObjectForKey:message.clientId];
[_localMembers removeObject:message];
}
}

- (BOOL)isNewestPresence:(nonnull ARTPresenceMessage *)received comparingWith:(ARTPresenceMessage *)latest __attribute__((warn_unused_result)) {
if (latest == nil) {
return YES;
Expand Down Expand Up @@ -100,41 +137,67 @@ - (BOOL)isNewestPresence:(nonnull ARTPresenceMessage *)received comparingWith:(A
else if (receivedMsgSerial == latestRegisteredMsgSerial && receivedIndex > latestRegisteredIndex) {
return YES;
}

[_logger debug:__FILE__ line:__LINE__ message:@"Presence member \"%@\" with action %@ has been ignored", received.memberKey, ARTPresenceActionToStr(received.action)];
return NO;
}

return !received.timestamp ||
[latest.timestamp timeIntervalSince1970] <= [received.timestamp timeIntervalSince1970];
}

- (void)clean {
for (NSString *key in [self.recentMembers allKeys]) {
ARTPresenceMessage *message = [self.recentMembers objectForKey:key];
if (message.action == ARTPresenceAbsent || message.action == ARTPresenceLeave) {
[self.recentMembers removeObjectForKey:key];
- (void)cleanUpAbsentMembers {
NSSet<NSString *> *filteredMembers = [_members keysOfEntriesPassingTest:^BOOL(NSString *key, ARTPresenceMessage *message, BOOL *stop) {
return message.action == ARTPresenceAbsent;
}];
for (NSString *key in filteredMembers) {
[self internalRemove:[_members objectForKey:key] force:true];
}
}

- (void)leaveMembersNotPresentInSync {
for (ARTPresenceMessage *member in [_members allValues]) {
if (member.syncSessionId != _syncSessionId) {
// Handle members that have not been added or updated in the PresenceMap during the sync process
ARTPresenceMessage *leave = [member copy];
[self internalRemove:member];
[self.delegate map:self didRemovedMemberNoLongerPresent:leave];
}
}
}

- (void)reenterLocalMembersMissingFromSync {
NSSet *filteredLocalMembers = [_localMembers filteredSetUsingPredicate:[NSPredicate predicateWithFormat:@"syncSessionId != %lu", (unsigned long)_syncSessionId]];
for (ARTPresenceMessage *localMember in filteredLocalMembers) {
ARTPresenceMessage *reenter = [localMember copy];
[self internalRemove:localMember];
[self.delegate map:self shouldReenterLocalMember:reenter];
}
}

- (void)reset {
_members = [NSMutableDictionary dictionary];
_localMembers = [NSMutableSet set];
}

- (void)startSync {
_recentMembers = [NSMutableDictionary dictionary];
_syncStarted = true;
_syncComplete = false;
_syncSessionId++;
_syncState = ARTPresenceSyncStarted;
[_syncEventEmitter emit:[NSNumber numberWithInt:ARTPresenceSyncStarted] with:nil];
}

- (void)endSync {
[self clean];
_syncStarted = false;
_syncComplete = true;
[_syncEventEmitter emit:[NSNumber numberWithInt:ARTPresenceSyncEnded] with:[self.recentMembers allValues]];
[self cleanUpAbsentMembers];
[self leaveMembersNotPresentInSync];
_syncState = ARTPresenceSyncEnded;
[self reenterLocalMembersMissingFromSync];
[_syncEventEmitter emit:[NSNumber numberWithInt:ARTPresenceSyncEnded] with:[_members allValues]];
[_syncEventEmitter off];
}

- (void)failsSync:(ARTErrorInfo *)error {
[self clean];
_syncStarted = false;
_syncComplete = true;
[self reset];
_syncState = ARTPresenceSyncFailed;
[_syncEventEmitter emit:[NSNumber numberWithInt:ARTPresenceSyncFailed] with:error];
[_syncEventEmitter off];
}
Expand All @@ -144,7 +207,7 @@ - (void)onceSyncEnds:(void (^)(NSArray<ARTPresenceMessage *> *))callback {
[_syncEventEmitter once:[NSNumber numberWithInt:ARTPresenceSyncEnded] callback:callback];
}
else {
callback([self.recentMembers allValues]);
callback([_members allValues]);
}
}

Expand All @@ -154,8 +217,12 @@ - (void)onceSyncFails:(void (^)(ARTErrorInfo *))callback {
}
}

- (BOOL)getSyncInProgress {
return _syncStarted && !_syncComplete;
- (BOOL)syncComplete {
return !(_syncState == ARTPresenceSyncInitialized || _syncState == ARTPresenceSyncStarted);
}

- (BOOL)syncInProgress {
return _syncState == ARTPresenceSyncStarted;
}

#pragma mark private
Expand Down
15 changes: 15 additions & 0 deletions Source/ARTPresenceMessage+Private.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//
// ARTPresenceMessage+Private.h
// Ably
//
// Created by Ricardo Pereira on 1/2/17.
// Copyright © 2017 Ably. All rights reserved.
//

#import "ARTPresenceMessage.h"

@interface ARTPresenceMessage ()

@property (readwrite, assign, nonatomic) NSUInteger syncSessionId;

@end
10 changes: 7 additions & 3 deletions Source/ARTPresenceMessage.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,19 @@ typedef NS_ENUM(NSUInteger, ARTPresenceAction) {
ARTPresenceUpdate
};

NSString *__art_nonnull ARTPresenceActionToStr(ARTPresenceAction action);
NSString *_Nonnull ARTPresenceActionToStr(ARTPresenceAction action);

ART_ASSUME_NONNULL_BEGIN

/// List of members present on a channel
@interface ARTPresenceMessage : ARTBaseMessage

@property (readwrite, assign, nonatomic) ARTPresenceAction action;

- (NSString *)memberKey;
- (nonnull NSString *)memberKey;

- (BOOL)isEqualToPresenceMessage:(ARTPresenceMessage *)presence;
- (BOOL)isEqualToPresenceMessage:(nonnull ARTPresenceMessage *)presence;

@end

ART_ASSUME_NONNULL_END
Loading

0 comments on commit 18e43ea

Please sign in to comment.