@@ -32,13 +32,18 @@
#import " SPObjectAdditions.h"
#import < SPMySQL/SPMySQLStreamingResultStore.h>
#include < stdlib.h>
#include < mach/mach_time.h>
@interface SPDataStorage (Private_API)
- (void ) _checkNewRow : (NSMutableArray *)aRow ;
- (void )_recordClearingUnloadedColumnsAt : (uint64_t )now from : (NSArray *)callStack ;
- (void )_assesUnloadedColumnsIsSet ;
@end
static uint64_t _elapsedMilliSecondsSinceAbsoluteTime (uint64_t comparisonTime);
@implementation SPDataStorage
static inline NSMutableArray * SPDataStorageGetEditedRow (NSPointerArray * rowStore, NSUInteger rowIndex)
@@ -60,7 +65,13 @@ - (void) setDataStorage:(SPMySQLStreamingResultStore *)newDataStorage updatingEx
NSUInteger i;
editedRowCount = 0 ;
SPClear (editedRows);
if (unloadedColumns) free (unloadedColumns), unloadedColumns = NULL ;
@synchronized (self) {
if (unloadedColumns) {
[self _recordClearingUnloadedColumnsAt: mach_absolute_time () from: [NSThread callStackSymbols ]];
free (unloadedColumns), unloadedColumns = NULL ;
}
}
if (dataStorage) {
@@ -81,9 +92,11 @@ - (void) setDataStorage:(SPMySQLStreamingResultStore *)newDataStorage updatingEx
[self resultStoreDidFinishLoadingData: dataStorage];
}
unloadedColumns = calloc (numberOfColumns, sizeof (BOOL ));
for (i = 0 ; i < numberOfColumns; i++) {
unloadedColumns[i] = NO ;
@synchronized (self) {
unloadedColumns = calloc (numberOfColumns, sizeof (BOOL ));
for (i = 0 ; i < numberOfColumns; i++) {
unloadedColumns[i] = NO ;
}
}
}
@@ -110,10 +123,12 @@ - (NSMutableArray *) rowContentsAtIndex:(NSUInteger)anIndex
NSMutableArray *dataArray = SPMySQLResultStoreGetRow (dataStorage, anIndex);
// Modify unloaded cells as appropriate
for (NSUInteger i = 0 ; i < numberOfColumns; i++) {
NSAssert (unloadedColumns != NULL , @" unloadedColumns not loaded!" );
if (unloadedColumns[i]) {
CFArraySetValueAtIndex ((CFMutableArrayRef )dataArray, i, [SPNotLoaded notLoaded ]);
@synchronized (self) {
[self _assesUnloadedColumnsIsSet ];
for (NSUInteger i = 0 ; i < numberOfColumns; i++) {
if (unloadedColumns[i]) {
CFArraySetValueAtIndex ((CFMutableArrayRef )dataArray, i, [SPNotLoaded notLoaded ]);
}
}
}
@@ -140,9 +155,11 @@ - (id) cellDataAtRow:(NSUInteger)rowIndex column:(NSUInteger)columnIndex
}
// If the specified column is not loaded, return a SPNotLoaded reference
NSAssert (unloadedColumns != NULL , @" unloadedColumns not loaded!" );
if (unloadedColumns[columnIndex]) {
return [SPNotLoaded notLoaded ];
@synchronized (self) {
[self _assesUnloadedColumnsIsSet ];
if (unloadedColumns[columnIndex]) {
return [SPNotLoaded notLoaded ];
}
}
// Return the content
@@ -175,9 +192,11 @@ - (id) cellPreviewAtRow:(NSUInteger)rowIndex column:(NSUInteger)columnIndex prev
}
// If the specified column is not loaded, return a SPNotLoaded reference
NSAssert (unloadedColumns != NULL , @" unloadedColumns not loaded!" );
if (unloadedColumns[columnIndex]) {
return [SPNotLoaded notLoaded ];
@synchronized (self) {
[self _assesUnloadedColumnsIsSet ];
if (unloadedColumns[columnIndex]) {
return [SPNotLoaded notLoaded ];
}
}
// Return the content
@@ -203,9 +222,11 @@ - (BOOL) cellIsNullOrUnloadedAtRow:(NSUInteger)rowIndex column:(NSUInteger)colum
[NSException raise :NSRangeException format: @" Requested storage column (col %llu ) beyond bounds (%llu )" , (unsigned long long )columnIndex, (unsigned long long )numberOfColumns];
}
NSAssert (unloadedColumns != NULL , @" unloadedColumns not loaded!" );
if (unloadedColumns[columnIndex]) {
return YES ;
@synchronized (self) {
[self _assesUnloadedColumnsIsSet ];
if (unloadedColumns[columnIndex]) {
return YES ;
}
}
return [dataStorage cellIsNullAtRow: rowIndex column: columnIndex];
@@ -236,10 +257,12 @@ - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state object
targetRow = SPMySQLResultStoreGetRow (dataStorage, state->state );
// Modify unloaded cells as appropriate
for (NSUInteger i = 0 ; i < numberOfColumns; i++) {
NSAssert (unloadedColumns != NULL , @" unloadedColumns not loaded!" );
if (unloadedColumns[i]) {
CFArraySetValueAtIndex ((CFMutableArrayRef )targetRow, i, [SPNotLoaded notLoaded ]);
@synchronized (self) {
[self _assesUnloadedColumnsIsSet ];
for (NSUInteger i = 0 ; i < numberOfColumns; i++) {
if (unloadedColumns[i]) {
CFArraySetValueAtIndex ((CFMutableArrayRef )targetRow, i, [SPNotLoaded notLoaded ]);
}
}
}
}
@@ -397,8 +420,10 @@ - (void) setColumnAsUnloaded:(NSUInteger)columnIndex
if (columnIndex >= numberOfColumns) {
[NSException raise :NSRangeException format: @" Invalid column set as unloaded; requested column index (%llu ) beyond bounds (%llu )" , (unsigned long long )columnIndex, (unsigned long long )numberOfColumns];
}
NSAssert (unloadedColumns != NULL , @" unloadedColumns not loaded!" );
unloadedColumns[columnIndex] = YES ;
@synchronized (self) {
[self _assesUnloadedColumnsIsSet ];
unloadedColumns[columnIndex] = YES ;
}
}
#pragma mark - Basic information
@@ -451,15 +476,29 @@ - (id) init {
numberOfColumns = 0 ;
editedRowCount = 0 ;
_debugInfo = nil ;
_debugTime = mach_absolute_time ();
}
return self;
}
- (void ) dealloc {
SPClear (dataStorage);
SPClear (editedRows);
if (unloadedColumns) free (unloadedColumns), unloadedColumns = NULL ;
@synchronized (self) {
if (unloadedColumns) {
[self _recordClearingUnloadedColumnsAt: mach_absolute_time () from: [NSThread callStackSymbols ]];
free (unloadedColumns), unloadedColumns = NULL ;
}
}
// this is very very unlikely, but if another thread had been waiting on the lock
// right before we free'd unloadedColumns, it should get it before we can release
// _debugInfo, too.
@synchronized (self) {
SPClear (_debugInfo);
}
[super dealloc ];
}
@@ -474,5 +513,37 @@ - (void) _checkNewRow:(NSMutableArray *)aRow
}
}
// DO NOT CALL THIS METHOD UNLESS YOU CURRENTLY HAVE A LOCK ON SELF!!!
- (void )_recordClearingUnloadedColumnsAt : (uint64_t )now from : (NSArray *)callStack
{
_debugTime = now;
SPClear (_debugInfo);
_debugInfo = [[NSString alloc ] initWithFormat: @" Thread: %@ , Stack: %@ " ,[NSThread currentThread ],callStack];
}
// DO NOT CALL THIS METHOD UNLESS YOU CURRENTLY HAVE A LOCK ON SELF!!!
- (void )_assesUnloadedColumnsIsSet
{
if (unloadedColumns != NULL )
return ;
uint64_t timeDiff = _elapsedMilliSecondsSinceAbsoluteTime (_debugTime);
NSString *msg;
if (!_debugInfo)
msg = [NSString stringWithFormat: @" unloadedColumns is not set and never has been since the object was created %llu ms ago." ,timeDiff];
else
msg = [NSString stringWithFormat: @" unloadedColumns was last cleared %llu ms ago at %@ " ,timeDiff,_debugInfo];
@throw [NSException exceptionWithName: NSInternalInconsistencyException reason: msg userInfo: nil ];
}
@end
static uint64_t _elapsedMilliSecondsSinceAbsoluteTime (uint64_t comparisonTime)
{
uint64_t elapsedTime_t = mach_absolute_time () - comparisonTime;
Nanoseconds elapsedTime = AbsoluteToNanoseconds (*(AbsoluteTime *)&(elapsedTime_t));
return (UnsignedWideToUInt64 (elapsedTime) / 1000000ULL );
}