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

+combineLatest:reduce: and -flatten: have serialization issues. #143

Closed
Coneko opened this issue Nov 27, 2012 · 6 comments
Closed

+combineLatest:reduce: and -flatten: have serialization issues. #143

Coneko opened this issue Nov 27, 2012 · 6 comments
Labels

Comments

@Coneko
Copy link
Member

Coneko commented Nov 27, 2012

Regardless of scheduler and subjects' serialization issues, methods that subscribe to multiple signals have serialization issues if the signals they subscribe to send from different threads.
+combineLatest:reduce: and -flatten: are affected for sure, +zip:reduce: doesn't seem to have issues since it synchronizes all subscriptions.

@jspahrsummers
Copy link
Member

I'm pretty sure this is captured by #138, since if those methods send nexts/error/completed to a serialized id<RACSubscriber>, it should work even if the original signals deliver on different threads.

@Coneko
Copy link
Member Author

Coneko commented Nov 27, 2012

Not if the implementation uses a different subscriber for each signal, which is what most of these methods do.

@jspahrsummers
Copy link
Member

I'm not sure I follow. Could you create a small example?

@Coneko
Copy link
Member Author

Coneko commented Nov 27, 2012

Hard to have a test case for it, but if you look at +combineLatest:reduce::

for(id<RACSignal> signal in signals) {

New RACSubscriber created for each signal

  RACDisposable *disposable = [signal subscribe:[RACSubscriber subscriberWithNext:^(id x) {

nexts are synchronized on lastValues

    @synchronized(lastValues) {
      [lastValues setObject:x ? : [RACTupleNil tupleNil] forKey:[NSString stringWithFormat:@"%p", signal]];

      if(lastValues.count == signals.count) {
        NSMutableArray *orderedValues = [NSMutableArray arrayWithCapacity:signals.count];
        for(id<RACSignal> o in signals) {
          [orderedValues addObject:[lastValues objectForKey:[NSString stringWithFormat:@"%p", o]]];
        }

        if (reduceBlock == NULL) {
          [subscriber sendNext:[RACTuple tupleWithObjectsFromArray:orderedValues]];
        } else {
          [subscriber sendNext:[RACBlockTrampoline invokeBlock:reduceBlock withArguments:orderedValues]];
        }
      }
    }
  } error:^(NSError *error) {

errors aren't synchronized

    [subscriber sendError:error];
  } completed:^{

completeds are synchronized on completedSignals

    @synchronized(completedSignals) {
      [completedSignals addObject:signal];
      if(completedSignals.count == signals.count) {
        [subscriber sendCompleted];
      }
    }
  }]];

  if(disposable != nil) {
    [disposables addObject:disposable];
  }
}

Since next, error and completed are delivered to different RACSubscribers, and are synchronized on different locks, subscriber could get deliveries from different threads concurrently.

@jspahrsummers
Copy link
Member

I can understand why the synchronization might result in delays, but subscriber – the ultimate subscriber that each intermediate one delivers to – could still serialize the messages it receives, which would solve the thrust of this issue.

@Coneko
Copy link
Member Author

Coneko commented Nov 28, 2012

Oh you're right, my bad.
Does this mean #136 is also covered by #138? It doesn't matter if a scheduler delivers concurrently if the subscriber which is the receiver of the delivery takes care of serializing the deliveries.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants