Skip to content

Commit

Permalink
- Fixes potential deadlock scenario by adding semaphore only when not…
Browse files Browse the repository at this point in the history
… running in dispatch queue manager serial queue.
  • Loading branch information
nishant-clevertap committed Jan 21, 2025
1 parent 26b6d4f commit abc4731
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 30 deletions.
1 change: 1 addition & 0 deletions CleverTapSDK/CTDispatchQueueManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ NS_ASSUME_NONNULL_BEGIN
- (instancetype _Nonnull)initWithConfig:(CleverTapInstanceConfig*)config;
- (void)runSerialAsync:(void (^)(void))taskBlock;
- (void)runOnNotificationQueue:(void (^)(void))taskBlock;
- (BOOL)inSerialQueue;

@end

Expand Down
6 changes: 0 additions & 6 deletions CleverTapSDK/EventDatabase/CTEventDatabase.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,6 @@ normalizedEventName:(NSString *)normalizedEventName
- (NSInteger)getEventCount:(NSString *)normalizedEventName
deviceID:(NSString *)deviceID;

- (NSInteger)getFirstTimestamp:(NSString *)normalizedEventName
deviceID:(NSString *)deviceID;

- (NSInteger)getLastTimestamp:(NSString *)normalizedEventName
deviceID:(NSString *)deviceID;

- (CleverTapEventDetail *)getEventDetail:(NSString *)normalizedEventName
deviceID:(NSString *)deviceID;

Expand Down
94 changes: 70 additions & 24 deletions CleverTapSDK/EventDatabase/CTEventDatabase.m
Original file line number Diff line number Diff line change
Expand Up @@ -221,9 +221,7 @@ - (NSInteger)getEventCount:(NSString *)normalizedEventName

const char *querySQL = "SELECT count FROM CTUserEventLogs WHERE normalizedEventName = ? AND deviceID = ?";
__block NSInteger count = -1;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

[self.dispatchQueueManager runSerialAsync:^{
void (^taskBlock)(void) = ^{
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(self->_eventDatabase, querySQL, -1, &statement, NULL) == SQLITE_OK) {
sqlite3_bind_text(statement, 1, [normalizedEventName UTF8String], -1, SQLITE_TRANSIENT);
Expand All @@ -240,10 +238,24 @@ - (NSInteger)getEventCount:(NSString *)normalizedEventName
} else {
CleverTapLogStaticInternal(@"SQL prepare query error: %s", sqlite3_errmsg(self->_eventDatabase));
}
dispatch_semaphore_signal(semaphore);
}];
};

if ([self.dispatchQueueManager inSerialQueue]) {
// If already on the serial queue, execute directly without semaphore
taskBlock();
} else {
// Otherwise, use semaphore for synchronous execution
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[self.dispatchQueueManager runSerialAsync:^{
taskBlock();
dispatch_semaphore_signal(semaphore);
}];
if (dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 3)) != 0) {
CleverTapLogStaticInternal(@"Timeout occurred while getting event count.");
return -1;
}
}

dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
return count;
}

Expand All @@ -256,9 +268,7 @@ - (CleverTapEventDetail *)getEventDetail:(NSString *)normalizedEventName

const char *querySQL = "SELECT eventName, normalizedEventName, count, firstTs, lastTs, deviceID FROM CTUserEventLogs WHERE normalizedEventName = ? AND deviceID = ?";
__block CleverTapEventDetail *eventDetail = nil;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

[self.dispatchQueueManager runSerialAsync:^{
void (^taskBlock)(void) = ^{
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(self->_eventDatabase, querySQL, -1, &statement, NULL) == SQLITE_OK) {
sqlite3_bind_text(statement, 1, [normalizedEventName UTF8String], -1, SQLITE_TRANSIENT);
Expand Down Expand Up @@ -287,10 +297,24 @@ - (CleverTapEventDetail *)getEventDetail:(NSString *)normalizedEventName
} else {
CleverTapLogStaticInternal(@"SQL prepare query error: %s", sqlite3_errmsg(self->_eventDatabase));
}
dispatch_semaphore_signal(semaphore);
}];
};

dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
if ([self.dispatchQueueManager inSerialQueue]) {
// If already on the serial queue, execute directly without semaphore
taskBlock();
} else {
// Otherwise, use semaphore for synchronous execution
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[self.dispatchQueueManager runSerialAsync:^{
taskBlock();
dispatch_semaphore_signal(semaphore);
}];
if (dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 3)) != 0) {
CleverTapLogStaticInternal(@"Timeout occurred while getting event detail.");
return nil;
}
}

return eventDetail;
}

Expand All @@ -302,9 +326,7 @@ - (CleverTapEventDetail *)getEventDetail:(NSString *)normalizedEventName

const char *querySQL = "SELECT eventName, normalizedEventName, count, firstTs, lastTs, deviceID FROM CTUserEventLogs WHERE deviceID = ?";
__block NSMutableArray *eventDataArray = [NSMutableArray array];
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

[self.dispatchQueueManager runSerialAsync:^{
void (^taskBlock)(void) = ^{
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(self->_eventDatabase, querySQL, -1, &statement, NULL) == SQLITE_OK) {
sqlite3_bind_text(statement, 1, [deviceID UTF8String], -1, SQLITE_TRANSIENT);
Expand Down Expand Up @@ -332,10 +354,23 @@ - (CleverTapEventDetail *)getEventDetail:(NSString *)normalizedEventName
} else {
CleverTapLogStaticInternal(@"SQL prepare query error: %s", sqlite3_errmsg(self->_eventDatabase));
}
dispatch_semaphore_signal(semaphore);
}];
};

dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
if ([self.dispatchQueueManager inSerialQueue]) {
// If already on the serial queue, execute directly without semaphore
taskBlock();
} else {
// Otherwise, use semaphore for synchronous execution
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[self.dispatchQueueManager runSerialAsync:^{
taskBlock();
dispatch_semaphore_signal(semaphore);
}];
if (dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 3)) != 0) {
CleverTapLogStaticInternal(@"Timeout occurred while getting all event details.");
return nil;
}
}
return [eventDataArray copy];
}

Expand Down Expand Up @@ -443,9 +478,7 @@ - (void)deleteLeastRecentlyUsedRows:(NSInteger)maxRowLimit

- (void)openDatabase {
NSString *databasePath = [self databasePath];
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

[self.dispatchQueueManager runSerialAsync:^{
void (^taskBlock)(void) = ^{
if (sqlite3_open_v2([databasePath UTF8String], &self->_eventDatabase, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, NULL) == SQLITE_OK) {
// Create table, check and update the version if needed
[self createTableWithCompletion:^(BOOL exists) {
Expand All @@ -454,9 +487,22 @@ - (void)openDatabase {
} else {
CleverTapLogStaticInternal(@"Failed to open database - CleverTap-Events.db");
}
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
};

if ([self.dispatchQueueManager inSerialQueue]) {
// If already on the serial queue, execute directly without semaphore
taskBlock();
} else {
// Otherwise, use semaphore for synchronous execution
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[self.dispatchQueueManager runSerialAsync:^{
taskBlock();
dispatch_semaphore_signal(semaphore);
}];
if (dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 3)) != 0) {
CleverTapLogStaticInternal(@"Timeout occurred while opening database.");
}
}
}

- (void)createTableWithCompletion:(void (^)(BOOL success))completion {
Expand Down

0 comments on commit abc4731

Please sign in to comment.