NSURLSession: optimized timout timer and fixed memory management

This commit is contained in:
Frederik Seiffert 2023-01-16 12:13:54 +01:00 committed by Frederik Seiffert
parent cb6c53b84d
commit 15499e1017
6 changed files with 108 additions and 143 deletions

View file

@ -155,7 +155,7 @@ curl_debug_function(CURL *handle, curl_infotype type, char *data,
text = [NSString stringWithUTF8String: data];
}
NSLog(@"%p %lu %d %@", o, [task taskIdentifier], type, text);
NSLog(@"%p %lu %d %@", o, (unsigned long)[task taskIdentifier], type, text);
return 0;
}
@ -234,17 +234,7 @@ curl_socket_function(void *userdata, curl_socket_t fd, curlsocktype type)
- (void) resetTimer
{
// simply create a new timer with the same queue, timeout and handler
// this must cancel the old handler and reset the timer
if (_timeoutTimer)
{
GSTimeoutSource *oldTimer = _timeoutTimer;
[oldTimer cancel];
_timeoutTimer = [[GSTimeoutSource alloc] initWithQueue: [oldTimer queue]
milliseconds: [oldTimer milliseconds]
handler: [oldTimer handler]];
RELEASE(oldTimer);
}
[_timeoutTimer setTimeout: [_timeoutTimer timeout]];
}
- (void) setupCallbacks
@ -415,7 +405,7 @@ curl_socket_function(void *userdata, curl_socket_t fd, curlsocktype type)
else
{
value = [NSString stringWithFormat: @"%@:%lu:%@",
originHost, port, host];
originHost, (unsigned long)port, host];
}
struct curl_slist *connect_to = NULL;

View file

@ -605,7 +605,6 @@ parseArgumentPart(NSString *part, NSString *name)
GSTimeoutSource *timeoutTimer;
timeoutTimer = [[GSTimeoutSource alloc] initWithQueue: [task workQueue]
milliseconds: timeoutInterval
handler:
^{
NSError *urlError;
@ -622,6 +621,7 @@ parseArgumentPart(NSString *part, NSString *name)
[client URLProtocol: self didFailWithError: urlError];
}
}];
[timeoutTimer setTimeout: timeoutInterval];
[_easyHandle setTimeoutTimer: timeoutTimer];
RELEASE(timeoutTimer);

View file

@ -77,12 +77,10 @@ typedef NS_ENUM(NSUInteger, GSSocketRegisterActionType) {
socket: (curl_socket_t)socket
queue: (dispatch_queue_t)queue
handler: (dispatch_block_t)handler;
- (void) createReadSourceWithSocket: (curl_socket_t)socket
queue: (dispatch_queue_t)queue
handler: (dispatch_block_t)handler;
- (void) createWriteSourceWithSocket: (curl_socket_t)socket
queue: (dispatch_queue_t)queue
handler: (dispatch_block_t)handler;
- (dispatch_source_t) createSourceWithType: (dispatch_source_type_t)type
socket: (curl_socket_t)socket
queue: (dispatch_queue_t)queue
handler: (dispatch_block_t)handler;
+ (instancetype) from: (void*)socketSourcePtr;

View file

@ -10,8 +10,7 @@
#import "Foundation/NSURLSession.h"
@interface GSMultiHandle ()
- (void) performActionForSocket: (int)socket;
- (void) readAndWriteAvailableDataOnSocket: (int)socket;
- (void) readAndWriteAvailableDataOnSocket: (curl_socket_t)socket;
- (void) readMessages;
- (void) completedTransferForEasyHandle: (CURL*)rawEasyHandle
easyCode: (int)easyCode;
@ -196,44 +195,27 @@ static int curl_timer_function(CURL *easyHandle, int timeout, void *userdata) {
// of milliseconds.
if (-1 == value)
{
[_timeoutSource cancel];
DESTROY(_timeoutSource);
}
else if (0 == value)
{
[_timeoutSource cancel];
DESTROY(_timeoutSource);
dispatch_async(_queue,
^{
[self timeoutTimerFired];
});
}
[_timeoutSource suspend];
}
else
{
if (nil == _timeoutSource || value != [_timeoutSource milliseconds])
if (!_timeoutSource)
{
[_timeoutSource cancel];
DESTROY(_timeoutSource);
_timeoutSource = [[GSTimeoutSource alloc] initWithQueue: _queue
milliseconds: value
handler: ^{
[self timeoutTimerFired];
}];
}
}
}
[_timeoutSource setTimeout: value];
}
}
- (void) performActionForSocket: (int)socket
{
[self readAndWriteAvailableDataOnSocket: socket];
}
- (void)timeoutTimerFired
- (void) timeoutTimerFired
{
[self readAndWriteAvailableDataOnSocket: CURL_SOCKET_TIMEOUT];
}
- (void) readAndWriteAvailableDataOnSocket: (int)socket
- (void) readAndWriteAvailableDataOnSocket: (curl_socket_t)socket
{
int runningHandlesCount = 0;
@ -312,7 +294,7 @@ static int curl_timer_function(CURL *easyHandle, int timeout, void *userdata) {
[handle transferCompletedWithError: err];
}
- (int32_t) registerWithSocket: (curl_socket_t)socket
- (int32_t) registerWithSocket: (curl_socket_t)socket
what: (int)what
socketSourcePtr: (void *)socketSourcePtr
{
@ -342,6 +324,7 @@ static int curl_timer_function(CURL *easyHandle, int timeout, void *userdata) {
&& GSSocketRegisterActionTypeUnregister == [action type])
{
DESTROY(socketSources);
curl_multi_assign(_rawHandle, socket, NULL);
}
if (nil != socketSources)
@ -350,7 +333,7 @@ static int curl_timer_function(CURL *easyHandle, int timeout, void *userdata) {
socket: socket
queue: _queue
handler: ^{
[self performActionForSocket: socket];
[self readAndWriteAvailableDataOnSocket: socket];
}];
}
@ -400,16 +383,11 @@ static int curl_timer_function(CURL *easyHandle, int timeout, void *userdata) {
{
switch (self.type)
{
case GSSocketRegisterActionTypeNone:
return false;
case GSSocketRegisterActionTypeRegisterRead:
return true;
case GSSocketRegisterActionTypeRegisterWrite:
return false;
case GSSocketRegisterActionTypeRegisterReadAndWrite:
return true;
case GSSocketRegisterActionTypeUnregister:
return false;
return YES;
default:
return NO;
}
}
@ -417,16 +395,11 @@ static int curl_timer_function(CURL *easyHandle, int timeout, void *userdata) {
{
switch (self.type)
{
case GSSocketRegisterActionTypeNone:
return false;
case GSSocketRegisterActionTypeRegisterRead:
return false;
case GSSocketRegisterActionTypeRegisterWrite:
return true;
case GSSocketRegisterActionTypeRegisterReadAndWrite:
return true;
case GSSocketRegisterActionTypeUnregister:
return false;
return YES;
default:
return NO;
}
}
@ -444,16 +417,15 @@ static int curl_timer_function(CURL *easyHandle, int timeout, void *userdata) {
if (_readSource)
{
dispatch_source_cancel(_readSource);
dispatch_release(_readSource);
}
_readSource = NULL;
if (_writeSource)
{
dispatch_source_cancel(_writeSource);
dispatch_release(_writeSource);
}
_writeSource = NULL;
[super dealloc];
}
@ -462,50 +434,40 @@ static int curl_timer_function(CURL *easyHandle, int timeout, void *userdata) {
queue: (dispatch_queue_t)queue
handler: (dispatch_block_t)handler
{
if ([action needsReadSource])
if (!_readSource && [action needsReadSource])
{
[self createReadSourceWithSocket: socket queue: queue handler: handler];
_readSource = [self createSourceWithType: DISPATCH_SOURCE_TYPE_READ
socket: socket
queue: queue
handler: handler];
}
if ([action needsWriteSource])
if (!_writeSource && [action needsWriteSource])
{
[self createWriteSourceWithSocket: socket queue: queue handler: handler];
_writeSource = [self createSourceWithType: DISPATCH_SOURCE_TYPE_WRITE
socket: socket
queue: queue
handler: handler];
}
}
- (void) createReadSourceWithSocket: (curl_socket_t)socket
queue: (dispatch_queue_t)queue
handler: (dispatch_block_t)handler
- (dispatch_source_t) createSourceWithType: (dispatch_source_type_t)type
socket: (curl_socket_t)socket
queue: (dispatch_queue_t)queue
handler: (dispatch_block_t)handler
{
dispatch_source_t s;
dispatch_source_t source;
if (_readSource)
{
return;
}
source = dispatch_source_create(type, socket, 0, queue);
dispatch_source_set_event_handler(source, handler);
dispatch_source_set_cancel_handler(source, ^{
dispatch_release(source);
});
dispatch_resume(source);
s = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, socket, 0, queue);
dispatch_source_set_event_handler(s, handler);
_readSource = s;
dispatch_resume(s);
return source;
}
- (void) createWriteSourceWithSocket: (curl_socket_t)socket
queue: (dispatch_queue_t)queue
handler: (dispatch_block_t)handler
{
dispatch_source_t s;
if (_writeSource)
{
return;
}
s = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, socket, 0, queue);
dispatch_source_set_event_handler(s, handler);
_writeSource = s;
dispatch_resume(s);
}
+ (instancetype) from: (void*)socketSourcePtr
{

View file

@ -11,24 +11,22 @@
*/
@interface GSTimeoutSource : NSObject
{
dispatch_source_t _rawSource;
NSInteger _milliseconds;
dispatch_queue_t _queue;
dispatch_block_t _handler;
dispatch_source_t _timer;
NSInteger _timeoutMs;
bool _isSuspended;
}
- (void) cancel;
- (NSInteger) milliseconds;
- (dispatch_queue_t) queue;
- (dispatch_block_t) handler;
- (instancetype) initWithQueue: (dispatch_queue_t)queue
milliseconds: (NSInteger)milliseconds
handler: (dispatch_block_t)handler;
- (NSInteger) timeout;
- (void) setTimeout: (NSInteger)timeoutMs;
- (void) suspend;
- (void) cancel;
@end
#endif

View file

@ -3,23 +3,19 @@
@implementation GSTimeoutSource
- (instancetype) initWithQueue: (dispatch_queue_t)queue
milliseconds: (NSInteger)milliseconds
handler: (dispatch_block_t)handler
{
if (nil != (self = [super init]))
{
_queue = queue;
_handler = Block_copy(handler);
_milliseconds = milliseconds;
_rawSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, _queue);
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_source_set_event_handler(timer, handler);
dispatch_source_set_cancel_handler(timer, ^{
dispatch_release(timer);
});
uint64_t delay = MAX(1, milliseconds - 1);
dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, delay * NSEC_PER_MSEC);
dispatch_source_set_timer(_rawSource, start, delay * NSEC_PER_MSEC, _milliseconds == 1 ? 1 * NSEC_PER_USEC : 1 * NSEC_PER_MSEC);
dispatch_source_set_event_handler(_rawSource, _handler);
dispatch_resume(_rawSource);
_timer = timer;
_timeoutMs = -1;
_isSuspended = YES;
}
return self;
}
@ -27,33 +23,54 @@
- (void) dealloc
{
[self cancel];
Block_release(_handler);
[super dealloc];
}
- (NSInteger) timeout
{
return _timeoutMs;
}
- (void) setTimeout: (NSInteger)timeoutMs
{
if (timeoutMs >= 0)
{
_timeoutMs = timeoutMs;
dispatch_source_set_timer(_timer,
dispatch_time(DISPATCH_TIME_NOW, timeoutMs * NSEC_PER_MSEC),
DISPATCH_TIME_FOREVER, // don't repeat
timeoutMs * 0.05); // 5% leeway
if (_isSuspended)
{
_isSuspended = NO;
dispatch_resume(_timer);
}
}
else
{
[self suspend];
}
}
- (void)suspend
{
if (!_isSuspended)
{
_isSuspended = YES;
_timeoutMs = -1;
dispatch_suspend(_timer);
}
}
- (void) cancel
{
if (_rawSource)
if (_timer)
{
dispatch_source_cancel(_rawSource);
dispatch_release(_rawSource);
_rawSource = NULL;
dispatch_source_cancel(_timer);
_timer = NULL; // released in cancel handler
}
}
- (NSInteger) milliseconds
{
return _milliseconds;
}
- (dispatch_queue_t) queue
{
return _queue;
}
- (dispatch_block_t) handler
{
return _handler;
}
@end