Skip to content

Commit 38dea62

Browse files
committed
Moving away from ring buffer to something that requires less memory (and is just as fast, if not faster).
1 parent 43f7e25 commit 38dea62

File tree

32 files changed

+984
-1011
lines changed

32 files changed

+984
-1011
lines changed

GCD/GCDAsyncSocket.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
@class GCDAsyncReadPacket;
1717
@class GCDAsyncWritePacket;
18-
@class GCDAsyncSocketRingBuffer;
18+
@class GCDAsyncSocketPreBuffer;
1919

2020
#if TARGET_OS_IPHONE
2121

@@ -129,7 +129,7 @@ typedef enum GCDAsyncSocketError GCDAsyncSocketError;
129129

130130
unsigned long socketFDBytesAvailable;
131131

132-
GCDAsyncSocketRingBuffer *preBuffer;
132+
GCDAsyncSocketPreBuffer *preBuffer;
133133

134134
#if TARGET_OS_IPHONE
135135
CFStreamClientContext streamContext;

GCD/GCDAsyncSocket.m

Lines changed: 56 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@
1919
#import <CFNetwork/CFNetwork.h>
2020
#endif
2121

22-
#import <mach/mach.h>
23-
2422
#import <arpa/inet.h>
2523
#import <fcntl.h>
2624
#import <ifaddrs.h>
@@ -265,10 +263,28 @@ + (uint16_t)portFromSockaddr6:(const struct sockaddr_in6 *)pSockaddr6;
265263
#pragma mark -
266264
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
267265

268-
@interface GCDAsyncSocketRingBuffer : NSObject
266+
/**
267+
* A PreBuffer is used when there is more data available on the socket
268+
* than is being requested by current read request.
269+
* In this case we slurp up all data from the socket (to minimize sys calls),
270+
* and store additional yet unread data in a "prebuffer".
271+
*
272+
* The prebuffer is entirely drained before we read from the socket again.
273+
* In other words, a large chunk of data is written is written to the prebuffer.
274+
* The prebuffer is then drained via a series of one or more reads (for subsequent read request(s)).
275+
*
276+
* A ring buffer was once used for this purpose.
277+
* But a ring buffer takes up twice as much memory as needed (double the size for mirroring).
278+
* In fact, it generally takes up more than twice the needed size as everything has to be rounded up to vm_page_size.
279+
* And since the prebuffer is always completely drained after being written to, a full ring buffer isn't needed.
280+
*
281+
* The current design is very simple and straight-forward, while also keeping memory requirements lower.
282+
**/
283+
284+
@interface GCDAsyncSocketPreBuffer : NSObject
269285
{
270-
uint8_t *ringBuffer;
271-
size_t ringBufferSize;
286+
uint8_t *preBuffer;
287+
size_t preBufferSize;
272288

273289
uint8_t *readPointer;
274290
uint8_t *writePointer;
@@ -295,127 +311,46 @@ - (void)reset;
295311

296312
@end
297313

298-
@implementation GCDAsyncSocketRingBuffer
299-
300-
static void allocate_ring_buffer(uint8_t **ringBufferPtr, size_t ringBufferSize)
301-
{
302-
uint8_t *ringBuffer = NULL;
303-
304-
do
305-
{
306-
kern_return_t result;
307-
308-
vm_address_t buffer;
309-
vm_size_t bufferSize = ringBufferSize;
310-
311-
result = vm_allocate(mach_task_self(), &buffer, (bufferSize*2), VM_FLAGS_ANYWHERE);
312-
if (result != ERR_SUCCESS)
313-
{
314-
LogCError(@"vm_allocate error: %d", result);
315-
316-
continue;
317-
}
318-
319-
result = vm_deallocate(mach_task_self(), (buffer+bufferSize), bufferSize);
320-
if (result != ERR_SUCCESS)
321-
{
322-
LogCError(@"vm_deallocate error: %d", result);
323-
324-
vm_deallocate(mach_task_self(), buffer, (bufferSize*2));
325-
continue;
326-
}
327-
328-
vm_address_t bufferMirror = buffer + bufferSize;
329-
vm_prot_t cur_protection;
330-
vm_prot_t max_protection;
331-
332-
result = vm_remap(mach_task_self(),
333-
&bufferMirror, // target address
334-
bufferSize, // target address size
335-
0, // target address alignment mask
336-
FALSE, // target address placement indicator
337-
mach_task_self(),
338-
buffer, // source address
339-
0, // copy
340-
&cur_protection, // unused
341-
&max_protection, // unused
342-
VM_INHERIT_DEFAULT);
343-
344-
if (result != ERR_SUCCESS)
345-
{
346-
// Race condition we're prepared for.
347-
// This may occasionally happen, which is why we do this in a loop.
348-
LogCInfo(@"vm_remap error: %d", result);
349-
350-
vm_deallocate(mach_task_self(), buffer, bufferSize);
351-
}
352-
else
353-
{
354-
ringBuffer = (uint8_t *)buffer;
355-
}
356-
357-
358-
} while (ringBuffer == NULL);
359-
360-
*ringBufferPtr = ringBuffer;
361-
}
314+
@implementation GCDAsyncSocketPreBuffer
362315

363316
- (id)initWithCapacity:(size_t)numBytes
364317
{
365318
if ((self = [super init]))
366319
{
367-
// Round up to nearest vm_page_size
368-
ringBufferSize = round_page(numBytes);
369-
370-
allocate_ring_buffer(&ringBuffer, ringBufferSize);
320+
preBufferSize = numBytes;
321+
preBuffer = malloc(preBufferSize);
371322

372-
readPointer = ringBuffer;
373-
writePointer = ringBuffer;
323+
readPointer = preBuffer;
324+
writePointer = preBuffer;
374325
}
375326
return self;
376327
}
377328

378329
- (void)dealloc
379330
{
380-
kern_return_t result =
381-
vm_deallocate(mach_task_self(), (vm_address_t)ringBuffer, (vm_size_t)(ringBufferSize*2));
382-
383-
if (result != ERR_SUCCESS)
384-
{
385-
LogError(@"vm_deallocate error: %d", result);
386-
}
331+
if (preBuffer)
332+
free(preBuffer);
387333
}
388334

389335
- (void)ensureCapacityForWrite:(size_t)numBytes
390336
{
391-
size_t availableSpace = ringBufferSize - (writePointer - readPointer);
337+
size_t availableSpace = preBufferSize - (writePointer - readPointer);
392338

393339
if (numBytes > availableSpace)
394340
{
395341
size_t additionalBytes = numBytes - availableSpace;
396342

397-
uint8_t *newRingBuffer = NULL;
398-
size_t newRingBufferSize = round_page(ringBufferSize + additionalBytes);
399-
400-
allocate_ring_buffer(&newRingBuffer, newRingBufferSize);
343+
size_t newPreBufferSize = preBufferSize + additionalBytes;
344+
uint8_t *newPreBuffer = realloc(preBuffer, newPreBufferSize);
401345

402-
size_t availableBytes = writePointer - readPointer;
346+
size_t readPointerOffset = readPointer - preBuffer;
347+
size_t writePointerOffset = writePointer - preBuffer;
403348

404-
memcpy((void *)newRingBuffer, (const void *)readPointer, (unsigned long)(availableBytes));
349+
preBuffer = newPreBuffer;
350+
preBufferSize = newPreBufferSize;
405351

406-
kern_return_t result =
407-
vm_deallocate(mach_task_self(), (vm_address_t)ringBuffer, (vm_size_t)(ringBufferSize*2));
408-
409-
if (result != ERR_SUCCESS)
410-
{
411-
LogError(@"vm_deallocate error: %d", result);
412-
}
413-
414-
ringBuffer = newRingBuffer;
415-
ringBufferSize = newRingBufferSize;
416-
417-
readPointer = ringBuffer;
418-
writePointer = ringBuffer + availableBytes;
352+
readPointer = preBuffer + readPointerOffset;
353+
writePointer = preBuffer + writePointerOffset;
419354
}
420355
}
421356

@@ -435,9 +370,21 @@ - (void)getReadBuffer:(uint8_t **)bufferPtr availableBytes:(size_t *)availableBy
435370
if (availableBytesPtr) *availableBytesPtr = writePointer - readPointer;
436371
}
437372

373+
- (void)didRead:(size_t)bytesRead
374+
{
375+
readPointer += bytesRead;
376+
377+
if (readPointer == writePointer)
378+
{
379+
// The prebuffer has been drained. Reset pointers.
380+
readPointer = preBuffer;
381+
writePointer = preBuffer;
382+
}
383+
}
384+
438385
- (size_t)availableSpace
439386
{
440-
return ringBufferSize - (writePointer - readPointer);
387+
return preBufferSize - (writePointer - readPointer);
441388
}
442389

443390
- (uint8_t *)writeBuffer
@@ -448,18 +395,7 @@ - (uint8_t *)writeBuffer
448395
- (void)getWriteBuffer:(uint8_t **)bufferPtr availableSpace:(size_t *)availableSpacePtr
449396
{
450397
if (bufferPtr) *bufferPtr = writePointer;
451-
if (availableSpacePtr) *availableSpacePtr = ringBufferSize - (writePointer - readPointer);
452-
}
453-
454-
- (void)didRead:(size_t)bytesRead
455-
{
456-
readPointer += bytesRead;
457-
458-
if (readPointer >= (ringBuffer + ringBufferSize))
459-
{
460-
readPointer -= ringBufferSize;
461-
writePointer -= ringBufferSize;
462-
}
398+
if (availableSpacePtr) *availableSpacePtr = preBufferSize - (writePointer - readPointer);
463399
}
464400

465401
- (void)didWrite:(size_t)bytesWritten
@@ -469,8 +405,8 @@ - (void)didWrite:(size_t)bytesWritten
469405

470406
- (void)reset
471407
{
472-
readPointer = ringBuffer;
473-
writePointer = ringBuffer;
408+
readPointer = preBuffer;
409+
writePointer = preBuffer;
474410
}
475411

476412
@end
@@ -514,7 +450,7 @@ - (NSUInteger)optimalReadLengthWithDefault:(NSUInteger)defaultValue shouldPreBuf
514450

515451
- (NSUInteger)readLengthForNonTermWithHint:(NSUInteger)bytesAvailable;
516452
- (NSUInteger)readLengthForTermWithHint:(NSUInteger)bytesAvailable shouldPreBuffer:(BOOL *)shouldPreBufferPtr;
517-
- (NSUInteger)readLengthForTermWithPreBuffer:(GCDAsyncSocketRingBuffer *)preBuffer found:(BOOL *)foundPtr;
453+
- (NSUInteger)readLengthForTermWithPreBuffer:(GCDAsyncSocketPreBuffer *)preBuffer found:(BOOL *)foundPtr;
518454

519455
- (NSInteger)searchForTermAfterPreBuffering:(ssize_t)numBytes;
520456

@@ -778,7 +714,7 @@ - (NSUInteger)readLengthForTermWithHint:(NSUInteger)bytesAvailable shouldPreBuff
778714
*
779715
* It is assumed the terminator has not already been read.
780716
**/
781-
- (NSUInteger)readLengthForTermWithPreBuffer:(GCDAsyncSocketRingBuffer *)preBuffer found:(BOOL *)foundPtr
717+
- (NSUInteger)readLengthForTermWithPreBuffer:(GCDAsyncSocketPreBuffer *)preBuffer found:(BOOL *)foundPtr
782718
{
783719
NSAssert(term != nil, @"This method does not apply to non-term reads");
784720
NSAssert([preBuffer availableBytes] > 0, @"Invoked with empty pre buffer!");
@@ -1070,7 +1006,7 @@ - (id)initWithDelegate:(id)aDelegate delegateQueue:(dispatch_queue_t)dq socketQu
10701006
writeQueue = [[NSMutableArray alloc] initWithCapacity:5];
10711007
currentWrite = nil;
10721008

1073-
preBuffer = [[GCDAsyncSocketRingBuffer alloc] initWithCapacity:vm_page_size];
1009+
preBuffer = [[GCDAsyncSocketPreBuffer alloc] initWithCapacity:(1024 * 4)];
10741010
}
10751011
return self;
10761012
}

0 commit comments

Comments
 (0)