Skip to content

Commit

Permalink
Thread safety (#586)
Browse files Browse the repository at this point in the history
* New EventEmitter (using NSNotificationCenter)

 - In most ways that matter NSNotificationCenter is thread safe. You
can add/remove observers from any thread and you can post notifications
from any thread.

* Events for the EventEmitter

* Fix: should cancel timers when connection times out

* Fix: new state change can occur before receiving publishing acknowledgement

* Test suite: async forced transitions

* Test suite: ack order

* Test suite: stop when there's no internet

* Fix: instance objects released to soon

* Performed a static analysis from Xcode

* fixup! Test suite: ack order

* Memory leak: call session invalidate to dispose of its strong reference to the delegate

* fixup! Test suite: ack order

* Fix RTN19a: guarantee of a new transport (check transport reference)

* Fix: ACK or NACK has not yet been received for a message, the client should consider the delivery of those messages as failed

* Enhance RTN14b: better timings

* Fix: REST and Realtime, wait for last operation to release the object

* fixup! Test suite: ack order

* Fix: cancel timers when a connection gets closed

* fixup! Test suite: ack order

* Test suite: timings

* fixup! Enhance RTN14b: better timings

* Test suite: close connections

* Fix: turn off immediately reachability when close occurs

* Fix RTC1d: wait for host is not reachable error

* fixup! Test suite: ack order

* Travis update

* Fix RTN19a
  • Loading branch information
ricardopereira authored Mar 23, 2017
1 parent 9cd23ba commit a31ad3a
Show file tree
Hide file tree
Showing 56 changed files with 1,012 additions and 642 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ script:
# Use `travis_wait` when a long running command or compile step regularly takes longer than 10 minutes without producing any output.
# It writes a short line to the build log every minute for 20 minutes, extending the amount of time your command has to finish.
# Prefix `travis_wait` with a greater number to extend the wait time.
- travis_wait 30 scan --scheme "Ably" --open_report false
- scan --scheme "Ably" --open_report false --devices "iPhone 6s"
- bash ./Scripts/run_examples_tests.sh
6 changes: 4 additions & 2 deletions Examples/Tests/TestsTests/TestsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,17 +64,19 @@ class TestsTests: XCTestCase {
self.waitForExpectationsWithTimeout(10, handler: nil)

let backgroundRealtimeExpectation = self.expectationWithDescription("Realtime in a Background Queue")
var realtime: ARTRealtime! //strong reference
NSURLSession.sharedSession().dataTaskWithURL(NSURL(string:"https://ably.io")!) { _ in
let realtime = ARTRealtime(key: key as String)
realtime = ARTRealtime(key: key as String)
realtime.channels.get("foo").attach { _ in
defer { backgroundRealtimeExpectation.fulfill() }
}
}.resume()
self.waitForExpectationsWithTimeout(10, handler: nil)

let backgroundRestExpectation = self.expectationWithDescription("Rest in a Background Queue")
var rest: ARTRest! //strong reference
NSURLSession.sharedSession().dataTaskWithURL(NSURL(string:"https://ably.io")!) { _ in
let rest = ARTRest(key: key as String)
rest = ARTRest(key: key as String)
rest.channels.get("foo").history { _ in
defer { backgroundRestExpectation.fulfill() }
}
Expand Down
11 changes: 9 additions & 2 deletions Source/ARTAuth+Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ ART_ASSUME_NONNULL_BEGIN

/// Messages related to the ARTAuth
@protocol ARTAuthDelegate <NSObject>
@property (nonatomic, readonly) __GENERIC(ARTEventEmitter, NSNumber * /*ARTAuthorizationState*/, id) *authorizationEmitter;
@property (nonatomic, readonly) ARTEventEmitter<ARTEvent *, id> *authorizationEmitter;
- (void)auth:(ARTAuth *)auth didAuthorize:(ARTTokenDetails *)tokenDetails;
@end

Expand All @@ -42,7 +42,7 @@ ART_ASSUME_NONNULL_BEGIN
- (ARTTokenParams *)mergeParams:(ARTTokenParams *)customParams;

- (NSURL *)buildURL:(ARTAuthOptions *)options withParams:(ARTTokenParams *)params;
- (NSMutableURLRequest *)buildRequest:(ARTAuthOptions *)options withParams:(ARTTokenParams *)params;
- (NSMutableURLRequest *)buildRequest:(nullable ARTAuthOptions *)options withParams:(nullable ARTTokenParams *)params;

// Execute the received ARTTokenRequest
- (void)executeTokenRequest:(ARTTokenRequest *)tokenRequest callback:(void (^)(ARTTokenDetails *__art_nullable tokenDetails, NSError *__art_nullable error))callback;
Expand Down Expand Up @@ -70,4 +70,11 @@ ART_ASSUME_NONNULL_BEGIN

@end

#pragma mark - ARTEvent

@interface ARTEvent (AuthorizationState)
- (instancetype)initWithAuthorizationState:(ARTAuthorizationState)value;
+ (instancetype)newWithAuthorizationState:(ARTAuthorizationState)value;
@end

ART_ASSUME_NONNULL_END
32 changes: 29 additions & 3 deletions Source/ARTAuth.m
Original file line number Diff line number Diff line change
Expand Up @@ -377,11 +377,11 @@ - (void)authorize:(ARTTokenParams *)tokenParams options:(ARTAuthOptions *)authOp
if (lastDelegate) {
// Only the last request should remain
[lastDelegate.authorizationEmitter off];
[lastDelegate.authorizationEmitter once:[NSNumber numberWithInt:ARTAuthorizationSucceeded] callback:^(id null) {
[lastDelegate.authorizationEmitter once:[ARTEvent newWithAuthorizationState:ARTAuthorizationSucceeded] callback:^(id null) {
successBlock(_tokenDetails);
[lastDelegate.authorizationEmitter off];
}];
[lastDelegate.authorizationEmitter once:[NSNumber numberWithInt:ARTAuthorizationFailed] callback:^(NSError *error) {
[lastDelegate.authorizationEmitter once:[ARTEvent newWithAuthorizationState:ARTAuthorizationFailed] callback:^(NSError *error) {
failureBlock(error);
[lastDelegate.authorizationEmitter off];
}];
Expand All @@ -402,7 +402,10 @@ - (void)authorize:(ARTTokenParams *)tokenParams options:(ARTAuthOptions *)authOp
_tokenDetails = tokenDetails;
_method = ARTAuthMethodToken;

if (lastDelegate) {
if (!tokenDetails) {
failureBlock([ARTErrorInfo createWithCode:0 message:@"Token details are empty"]);
}
else if (lastDelegate) {
[lastDelegate auth:self didAuthorize:tokenDetails];
}
else {
Expand Down Expand Up @@ -506,3 +509,26 @@ - (void)toTokenDetails:(ARTAuth *)auth callback:(void (^)(ARTTokenDetails * _Nul
}

@end

NSString *ARTAuthorizationStateToStr(ARTAuthorizationState state) {
switch (state) {
case ARTAuthorizationSucceeded:
return @"Succeeded"; //0
case ARTAuthorizationFailed:
return @"Failed"; //1
}
}

#pragma mark - ARTEvent

@implementation ARTEvent (AuthorizationState)

- (instancetype)initWithAuthorizationState:(ARTAuthorizationState)value {
return [self initWithString:[NSString stringWithFormat:@"ARTAuthorizationState%@", ARTAuthorizationStateToStr(value)]];
}

+ (instancetype)newWithAuthorizationState:(ARTAuthorizationState)value {
return [[self alloc] initWithAuthorizationState:value];
}

@end
2 changes: 1 addition & 1 deletion Source/ARTChannels+Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ extern NSString* (^__art_nullable ARTChannels_getChannelNamePrefix)();

@protocol ARTChannelsDelegate <NSObject>

- (id)makeChannel:(NSString *)channel options:(ARTChannelOptions *)options;
- (id)makeChannel:(NSString *)channel options:(nullable ARTChannelOptions *)options;

@end

Expand Down
2 changes: 1 addition & 1 deletion Source/ARTConnection+Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ ART_ASSUME_NONNULL_BEGIN

@interface ARTConnection ()

@property (readonly, strong, nonatomic) __GENERIC(ARTEventEmitter, NSNumber *, ARTConnectionStateChange *) *eventEmitter;
@property (readonly, strong, nonatomic) ARTEventEmitter<ARTEvent *, ARTConnectionStateChange *> *eventEmitter;
@property(weak, nonatomic) ARTRealtime* realtime;

@end
Expand Down
7 changes: 7 additions & 0 deletions Source/ARTConnection.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,11 @@ ART_EMBED_INTERFACE_EVENT_EMITTER(ARTRealtimeConnectionEvent, ARTConnectionState

@end

#pragma mark - ARTEvent

@interface ARTEvent (ConnectionEvent)
- (instancetype)initWithConnectionEvent:(ARTRealtimeConnectionEvent)value;
+ (instancetype)newWithConnectionEvent:(ARTRealtimeConnectionEvent)value;
@end

ART_ASSUME_NONNULL_END
36 changes: 23 additions & 13 deletions Source/ARTConnection.m
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ @implementation ARTConnection {
}

- (instancetype)initWithRealtime:(ARTRealtime *)realtime {
if (self == [super init]) {
if (self = [super init]) {
_queue = dispatch_queue_create("io.ably.realtime.connection", DISPATCH_QUEUE_SERIAL);
_eventEmitter = [[ARTEventEmitter alloc] initWithQueue:_queue];
_realtime = realtime;
Expand Down Expand Up @@ -78,39 +78,49 @@ - (NSString *)getRecoveryKey {
}
}

- (__GENERIC(ARTEventListener, ARTConnectionStateChange *) *)on:(ARTRealtimeConnectionEvent)event callback:(void (^)(ARTConnectionStateChange *))cb {
return [_eventEmitter on:[NSNumber numberWithInt:event] callback:cb];
- (ARTEventListener *)on:(ARTRealtimeConnectionEvent)event callback:(void (^)(ARTConnectionStateChange *))cb {
return [_eventEmitter on:[ARTEvent newWithConnectionEvent:event] callback:cb];
}

- (__GENERIC(ARTEventListener, ARTConnectionStateChange *) *)on:(void (^)(ARTConnectionStateChange *))cb {
- (ARTEventListener *)on:(void (^)(ARTConnectionStateChange *))cb {
return [_eventEmitter on:cb];
}

- (__GENERIC(ARTEventListener, ARTConnectionStateChange *) *)once:(ARTRealtimeConnectionEvent)event callback:(void (^)(ARTConnectionStateChange *))cb {
return [_eventEmitter once:[NSNumber numberWithInt:event] callback:cb];
- (ARTEventListener *)once:(ARTRealtimeConnectionEvent)event callback:(void (^)(ARTConnectionStateChange *))cb {
return [_eventEmitter once:[ARTEvent newWithConnectionEvent:event] callback:cb];
}

- (__GENERIC(ARTEventListener, ARTConnectionStateChange *) *)once:(void (^)(ARTConnectionStateChange *))cb {
- (ARTEventListener *)once:(void (^)(ARTConnectionStateChange *))cb {
return [_eventEmitter once:cb];
}

- (void)off {
[_eventEmitter off];
}
- (void)off:(ARTRealtimeConnectionEvent)event listener:listener {
[_eventEmitter off:[NSNumber numberWithInt:event] listener:listener];
- (void)off:(ARTRealtimeConnectionEvent)event listener:(ARTEventListener *)listener {
[_eventEmitter off:[ARTEvent newWithConnectionEvent:event] listener:listener];
}

- (void)off:(__GENERIC(ARTEventListener, ARTConnectionStateChange *) *)listener {
- (void)off:(ARTEventListener *)listener {
[_eventEmitter off:listener];
}

- (void)emit:(ARTRealtimeConnectionEvent)event with:(ARTConnectionStateChange *)data {
[_eventEmitter emit:[NSNumber numberWithInt:event] with:data];
[_eventEmitter emit:[ARTEvent newWithConnectionEvent:event] with:data];
}

- (ARTEventListener *)timed:(ARTEventListener *)listener deadline:(NSTimeInterval)deadline onTimeout:(void (^)())onTimeout {
return [_eventEmitter timed:listener deadline:deadline onTimeout:onTimeout];
@end

#pragma mark - ARTEvent

@implementation ARTEvent (ConnectionEvent)

- (instancetype)initWithConnectionEvent:(ARTRealtimeConnectionEvent)value {
return [self initWithString:[NSString stringWithFormat:@"ARTRealtimeConnectionEvent%@", ARTRealtimeConnectionEventToStr(value)]];
}

+ (instancetype)newWithConnectionEvent:(ARTRealtimeConnectionEvent)value {
return [[self alloc] initWithConnectionEvent:value];
}

@end
2 changes: 1 addition & 1 deletion Source/ARTConnectionDetails.m
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ - (instancetype)initWithClientId:(NSString *__art_nullable)clientId
maxInboundRate:(NSInteger)maxInboundRate
connectionStateTtl:(NSTimeInterval)connectionStateTtl
serverId:(NSString *)serverId {
if (self == [super init]) {
if (self = [super init]) {
_clientId = clientId;
_connectionKey = connectionKey;
_maxMessageSize = maxMessageSize;
Expand Down
2 changes: 1 addition & 1 deletion Source/ARTCrypto+Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ ART_ASSUME_NONNULL_BEGIN
+ (int)defaultKeyLength;
+ (int)defaultBlockLength;

+ (NSData *)generateSecureRandomData:(size_t)length;
+ (nullable NSData *)generateSecureRandomData:(size_t)length;

+ (id<ARTChannelCipher>)cipherWithParams:(ARTCipherParams *)params;

Expand Down
2 changes: 1 addition & 1 deletion Source/ARTDataEncoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ ART_ASSUME_NONNULL_BEGIN

+ (NSString *)artAddEncoding:(NSString *)encoding toString:(NSString *__art_nullable)s;
- (NSString *)artLastEncoding;
- (NSString *)artRemoveLastEncoding;
- (nullable NSString *)artRemoveLastEncoding;

@end

Expand Down
20 changes: 7 additions & 13 deletions Source/ARTEventEmitter+Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,18 @@
//

#include "ARTEventEmitter.h"
#include "CompatibilityMacros.h"

ART_ASSUME_NONNULL_BEGIN
NS_ASSUME_NONNULL_BEGIN

@interface __GENERIC(ARTEventEmitterEntry, ItemType) : NSObject
@interface ARTEventEmitter<EventType, ItemType> ()

@property (readwrite, strong, nonatomic) __GENERIC(ARTEventListener, ItemType) *listener;
@property (readwrite, nonatomic) BOOL once;
@property (nonatomic, readonly) NSNotificationCenter *notificationCenter;
@property (nonatomic, readonly) dispatch_queue_t queue;

- (instancetype)initWithListener:(__GENERIC(ARTEventListener, ItemType) *)listener once:(BOOL)once;
@property (readonly, atomic) NSMutableDictionary<NSString *, NSMutableArray<ARTEventListener *> *> *listeners;
@property (readonly, atomic) NSMutableArray<ARTEventListener *> *anyListeners;

@end

@interface __GENERIC(ARTEventEmitter, EventType, ItemType) ()
NS_ASSUME_NONNULL_END

@property (readwrite, atomic) __GENERIC(NSMutableDictionary, EventType, __GENERIC(NSMutableArray, __GENERIC(ARTEventEmitterEntry, ItemType) *) *) *listeners;
@property (readwrite, atomic) __GENERIC(NSMutableArray, __GENERIC(ARTEventEmitterEntry, ItemType) *) *anyListeners;

@end

ART_ASSUME_NONNULL_END
Loading

0 comments on commit a31ad3a

Please sign in to comment.