Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support operation coallescing #141

Merged
merged 6 commits into from
Nov 17, 2016
Merged

Conversation

nguyenhuy
Copy link
Member

@nguyenhuy nguyenhuy commented Nov 11, 2016

Author: Huy Nguyen

Add a generic operation coallescing mechanism and use it to fix #135, #137.

@nguyenhuy
Copy link
Member Author

@garrettmoon @appleguy: Appreciate a review here :)

@nguyenhuy nguyenhuy force-pushed the 135_coalescing_operations branch from bceac42 to de1b23f Compare November 11, 2016 19:05
@nguyenhuy nguyenhuy force-pushed the 135_coalescing_operations branch from de1b23f to 21ae7a0 Compare November 16, 2016 19:56
typedef NS_ENUM(NSUInteger, PINDiskCacheCondition) {
PINDiskCacheConditionNotReady = 0,
PINDiskCacheConditionReady = 1,
};

static PINOperationDataCoallescingBlock PINTrimmingDataCoallescingBlock = ^id(id existingData, id newData) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This block works for both dates and bytes, right? I'm wondering if it would still be better to separate them so that we don't have to pass in ids. Is that possible? I'm not sure if Xcode will be happy with upcasting, but if it is, it would make this more readable even though it's duplicated code.

On another note, should they be named with PINDisk prefixing them?

dispatch_block_t completion = nil;
if (block) {
completion = ^{
PINDiskCache *strongSelf = weakSelf;
block(strongSelf);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because we guarantee the instance of PINDiskCache to be non-null, I think we may need to not weakify here. I realize it was wrong before.

dispatch_block_t completion = nil;
if (block) {
completion = ^{
PINDiskCache *strongSelf = weakSelf;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing here, we shouldn't weakify / strongify I don't think.

dispatch_block_t completion = nil;
if (block) {
completion = ^{
PINDiskCache *strongSelf = weakSelf;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No weakify / strongify

@@ -27,6 +30,7 @@ typedef NS_ENUM(NSUInteger, PINOperationQueuePriority) {

- (id <PINOperationReference>)addOperation:(dispatch_block_t)operation;
- (id <PINOperationReference>)addOperation:(dispatch_block_t)operation withPriority:(PINOperationQueuePriority)priority;
- (id <PINOperationReference>)addOperation:(PINOperationBlock)operation withPriority:(PINOperationQueuePriority)priority identifier:(NSString *)identifier data:(nullable id)data dataCoallescingBlock:(nullable PINOperationDataCoallescingBlock)dataCoallescingBlock completion:(nullable dispatch_block_t)completion;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mind putting newlines between the params? This is getting a bit hairy :)

[_queuedOperations addObject:operation];
[_referenceToOperations setObject:operation forKey:reference];
if (identifier != nil && (operation = [_identifierToOperations objectForKey:identifier]) != nil) {
// There is an exisiting operation with the provided identifier, let's coallescing these operations
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: should be "let's coalesce these operations"

} else {
operation.completion = ^{
initialCompletion();
completion();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm worried that if you had a really large number of operations this could cause a significant overhead. Should it be an array of completions instead? That way it doesn't need to be nested within blocks?

if (identifier != nil && (operation = [_identifierToOperations objectForKey:identifier]) != nil) {
// There is an exisiting operation with the provided identifier, let's coallescing these operations
if (dataCoallescingBlock != nil) {
operation.data = dataCoallescingBlock(operation.data, data);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should probably unlock while calling this method in case someone tries to do something very expensive. We'd probably need to grab a reference to operation.data before unlocking.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm worried that if we release the lock here, the operation might be grabbed on another thread and executed. That's the last think we want to happen.


return reference;
return operation.reference;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it safe to access operation.reference without holding the lock? Should we grab a copy before unlocking? I guess since we don't mutate it anywhere it's safe, but we should add a comment to that effect at the very least?

@@ -98,6 +105,7 @@ - (instancetype)initWithMaxConcurrentOperations:(NSUInteger)maxConcurrentOperati
_highPriorityOperations = [[NSMutableOrderedSet alloc] init];

_referenceToOperations = [NSMapTable weakToWeakObjectsMapTable];
_identifierToOperations = [NSMapTable weakToWeakObjectsMapTable];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it safe for this to be weakToWeak? I think it may need to be weakToStrong, as we don't know if callers of the api will keep strong references to their identifiers (i.e. they may just pass in a locally allocated NSString which could fall out of scope).

I think that should be ok though because they'll fall out when the identifier gets released?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we're good here because the operation already keeps a strong reference to its identifier.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yeah :)

@garrettmoon
Copy link
Collaborator

I am soooo excited by this BTW.

@nguyenhuy nguyenhuy force-pushed the 135_coalescing_operations branch from 214165a to f195dd2 Compare November 17, 2016 17:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants