Skip to content

Commit 80f8cae

Browse files
committed
Additional debug code for #2163
1 parent 670758d commit 80f8cae

File tree

2 files changed

+99
-25
lines changed

2 files changed

+99
-25
lines changed

Diff for: Source/SPDataStorage.h

+3
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@
4747

4848
NSUInteger numberOfColumns;
4949
NSUInteger editedRowCount;
50+
51+
NSString *_debugInfo;
52+
uint64_t _debugTime;
5053
}
5154

5255
/* Setting result store */

Diff for: Source/SPDataStorage.m

+96-25
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,18 @@
3232
#import "SPObjectAdditions.h"
3333
#import <SPMySQL/SPMySQLStreamingResultStore.h>
3434
#include <stdlib.h>
35+
#include <mach/mach_time.h>
3536

3637
@interface SPDataStorage (Private_API)
3738

3839
- (void) _checkNewRow:(NSMutableArray *)aRow;
40+
- (void)_recordClearingUnloadedColumnsAt:(uint64_t)now from:(NSArray *)callStack;
41+
- (void)_assesUnloadedColumnsIsSet;
3942

4043
@end
4144

45+
static uint64_t _elapsedMilliSecondsSinceAbsoluteTime(uint64_t comparisonTime);
46+
4247
@implementation SPDataStorage
4348

4449
static inline NSMutableArray* SPDataStorageGetEditedRow(NSPointerArray* rowStore, NSUInteger rowIndex)
@@ -60,7 +65,13 @@ - (void) setDataStorage:(SPMySQLStreamingResultStore *)newDataStorage updatingEx
6065
NSUInteger i;
6166
editedRowCount = 0;
6267
SPClear(editedRows);
63-
if (unloadedColumns) free(unloadedColumns), unloadedColumns = NULL;
68+
@synchronized(self) {
69+
if (unloadedColumns) {
70+
[self _recordClearingUnloadedColumnsAt:mach_absolute_time() from:[NSThread callStackSymbols]];
71+
free(unloadedColumns), unloadedColumns = NULL;
72+
}
73+
}
74+
6475

6576
if (dataStorage) {
6677

@@ -81,9 +92,11 @@ - (void) setDataStorage:(SPMySQLStreamingResultStore *)newDataStorage updatingEx
8192
[self resultStoreDidFinishLoadingData:dataStorage];
8293
}
8394

84-
unloadedColumns = calloc(numberOfColumns, sizeof(BOOL));
85-
for (i = 0; i < numberOfColumns; i++) {
86-
unloadedColumns[i] = NO;
95+
@synchronized(self) {
96+
unloadedColumns = calloc(numberOfColumns, sizeof(BOOL));
97+
for (i = 0; i < numberOfColumns; i++) {
98+
unloadedColumns[i] = NO;
99+
}
87100
}
88101
}
89102

@@ -110,10 +123,12 @@ - (NSMutableArray *) rowContentsAtIndex:(NSUInteger)anIndex
110123
NSMutableArray *dataArray = SPMySQLResultStoreGetRow(dataStorage, anIndex);
111124

112125
// Modify unloaded cells as appropriate
113-
for (NSUInteger i = 0; i < numberOfColumns; i++) {
114-
NSAssert(unloadedColumns != NULL, @"unloadedColumns not loaded!");
115-
if (unloadedColumns[i]) {
116-
CFArraySetValueAtIndex((CFMutableArrayRef)dataArray, i, [SPNotLoaded notLoaded]);
126+
@synchronized(self) {
127+
[self _assesUnloadedColumnsIsSet];
128+
for (NSUInteger i = 0; i < numberOfColumns; i++) {
129+
if (unloadedColumns[i]) {
130+
CFArraySetValueAtIndex((CFMutableArrayRef)dataArray, i, [SPNotLoaded notLoaded]);
131+
}
117132
}
118133
}
119134

@@ -140,9 +155,11 @@ - (id) cellDataAtRow:(NSUInteger)rowIndex column:(NSUInteger)columnIndex
140155
}
141156

142157
// If the specified column is not loaded, return a SPNotLoaded reference
143-
NSAssert(unloadedColumns != NULL, @"unloadedColumns not loaded!");
144-
if (unloadedColumns[columnIndex]) {
145-
return [SPNotLoaded notLoaded];
158+
@synchronized(self) {
159+
[self _assesUnloadedColumnsIsSet];
160+
if (unloadedColumns[columnIndex]) {
161+
return [SPNotLoaded notLoaded];
162+
}
146163
}
147164

148165
// Return the content
@@ -175,9 +192,11 @@ - (id) cellPreviewAtRow:(NSUInteger)rowIndex column:(NSUInteger)columnIndex prev
175192
}
176193

177194
// If the specified column is not loaded, return a SPNotLoaded reference
178-
NSAssert(unloadedColumns != NULL, @"unloadedColumns not loaded!");
179-
if (unloadedColumns[columnIndex]) {
180-
return [SPNotLoaded notLoaded];
195+
@synchronized(self) {
196+
[self _assesUnloadedColumnsIsSet];
197+
if (unloadedColumns[columnIndex]) {
198+
return [SPNotLoaded notLoaded];
199+
}
181200
}
182201

183202
// Return the content
@@ -203,9 +222,11 @@ - (BOOL) cellIsNullOrUnloadedAtRow:(NSUInteger)rowIndex column:(NSUInteger)colum
203222
[NSException raise:NSRangeException format:@"Requested storage column (col %llu) beyond bounds (%llu)", (unsigned long long)columnIndex, (unsigned long long)numberOfColumns];
204223
}
205224

206-
NSAssert(unloadedColumns != NULL, @"unloadedColumns not loaded!");
207-
if (unloadedColumns[columnIndex]) {
208-
return YES;
225+
@synchronized(self) {
226+
[self _assesUnloadedColumnsIsSet];
227+
if (unloadedColumns[columnIndex]) {
228+
return YES;
229+
}
209230
}
210231

211232
return [dataStorage cellIsNullAtRow:rowIndex column:columnIndex];
@@ -236,10 +257,12 @@ - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state object
236257
targetRow = SPMySQLResultStoreGetRow(dataStorage, state->state);
237258

238259
// Modify unloaded cells as appropriate
239-
for (NSUInteger i = 0; i < numberOfColumns; i++) {
240-
NSAssert(unloadedColumns != NULL, @"unloadedColumns not loaded!");
241-
if (unloadedColumns[i]) {
242-
CFArraySetValueAtIndex((CFMutableArrayRef)targetRow, i, [SPNotLoaded notLoaded]);
260+
@synchronized(self) {
261+
[self _assesUnloadedColumnsIsSet];
262+
for (NSUInteger i = 0; i < numberOfColumns; i++) {
263+
if (unloadedColumns[i]) {
264+
CFArraySetValueAtIndex((CFMutableArrayRef)targetRow, i, [SPNotLoaded notLoaded]);
265+
}
243266
}
244267
}
245268
}
@@ -397,8 +420,10 @@ - (void) setColumnAsUnloaded:(NSUInteger)columnIndex
397420
if (columnIndex >= numberOfColumns) {
398421
[NSException raise:NSRangeException format:@"Invalid column set as unloaded; requested column index (%llu) beyond bounds (%llu)", (unsigned long long)columnIndex, (unsigned long long)numberOfColumns];
399422
}
400-
NSAssert(unloadedColumns != NULL, @"unloadedColumns not loaded!");
401-
unloadedColumns[columnIndex] = YES;
423+
@synchronized(self) {
424+
[self _assesUnloadedColumnsIsSet];
425+
unloadedColumns[columnIndex] = YES;
426+
}
402427
}
403428

404429
#pragma mark - Basic information
@@ -451,15 +476,29 @@ - (id) init {
451476

452477
numberOfColumns = 0;
453478
editedRowCount = 0;
479+
480+
_debugInfo = nil;
481+
_debugTime = mach_absolute_time();
454482
}
455483
return self;
456484
}
457485

458486
- (void) dealloc {
459487
SPClear(dataStorage);
460488
SPClear(editedRows);
461-
if (unloadedColumns) free(unloadedColumns), unloadedColumns = NULL;
462-
489+
@synchronized(self) {
490+
if (unloadedColumns) {
491+
[self _recordClearingUnloadedColumnsAt:mach_absolute_time() from:[NSThread callStackSymbols]];
492+
free(unloadedColumns), unloadedColumns = NULL;
493+
}
494+
}
495+
// this is very very unlikely, but if another thread had been waiting on the lock
496+
// right before we free'd unloadedColumns, it should get it before we can release
497+
// _debugInfo, too.
498+
@synchronized(self) {
499+
SPClear(_debugInfo);
500+
}
501+
463502
[super dealloc];
464503
}
465504

@@ -474,5 +513,37 @@ - (void) _checkNewRow:(NSMutableArray *)aRow
474513
}
475514
}
476515

516+
// DO NOT CALL THIS METHOD UNLESS YOU CURRENTLY HAVE A LOCK ON SELF!!!
517+
- (void)_recordClearingUnloadedColumnsAt:(uint64_t)now from:(NSArray *)callStack
518+
{
519+
_debugTime = now;
520+
SPClear(_debugInfo);
521+
_debugInfo = [[NSString alloc] initWithFormat:@"Thread: %@, Stack: %@",[NSThread currentThread],callStack];
522+
}
523+
524+
// DO NOT CALL THIS METHOD UNLESS YOU CURRENTLY HAVE A LOCK ON SELF!!!
525+
- (void)_assesUnloadedColumnsIsSet
526+
{
527+
if(unloadedColumns != NULL)
528+
return;
529+
530+
uint64_t timeDiff = _elapsedMilliSecondsSinceAbsoluteTime(_debugTime);
531+
532+
NSString *msg;
533+
if(!_debugInfo)
534+
msg = [NSString stringWithFormat:@"unloadedColumns is not set and never has been since the object was created %llums ago.",timeDiff];
535+
else
536+
msg = [NSString stringWithFormat:@"unloadedColumns was last cleared %llums ago at %@",timeDiff,_debugInfo];
537+
538+
@throw [NSException exceptionWithName:NSInternalInconsistencyException reason:msg userInfo:nil];
539+
}
477540

478541
@end
542+
543+
static uint64_t _elapsedMilliSecondsSinceAbsoluteTime(uint64_t comparisonTime)
544+
{
545+
uint64_t elapsedTime_t = mach_absolute_time() - comparisonTime;
546+
Nanoseconds elapsedTime = AbsoluteToNanoseconds(*(AbsoluteTime *)&(elapsedTime_t));
547+
548+
return (UnsignedWideToUInt64(elapsedTime) / 1000000ULL);
549+
}

0 commit comments

Comments
 (0)