Repository: ReactiveCocoa/ReactiveCocoa

Issue #1448: Infinite Recursion in Signal.current under Beta5
Created: 2014-08-07T06:58:04Z
I'm trying to get to grips with the changes in swift-development and I am currently getting a stack explosion when a Signal is initialized and its initial value is accessed:
let foo: Signal<String> = self.thingOne.signal.combineLatestWith(self.thingTwo.signal)
  .map { (first, second) -> String in
    return first + "  " + second
  }
  .map { (string) -> String in
    return string + " another"
  }

then:
public func combineLatestWith<U>(stream: Signal<U>) -> Signal<(T, U)> {
    return Signal<(T, U)>(initialValue: (self.current, stream.current)) { sink in
        // FIXME: This implementation is probably racey.
        self.observe { [unowned stream] value in sink.put(value, stream.current) }
        stream.observe { [unowned self] value in sink.put(self.current, value) }
    }
}

With the recursion occuring in Signal.m
public var current: T {
    var value: T? = nil

    dispatch_sync(queue) {
        value = self.current
    }

    return value!
}

frame #10: 0x000000011132b21c ReactiveCocoa`ReactiveCocoa.Signal.current.getter : A(self=0x000000011e7c0000) + 412 at Signal.swift:26
frame #11: 0x0000000111334ec3 ReactiveCocoa`ReactiveCocoa.Signal.(value=Swift.Optional<T> at 0x00007fff508525a0, self=0x000000011e7c0000).(closure #1) + 147 at Signal.swift:27
frame #12: 0x00000001113350f7 ReactiveCocoa`reabstraction thunk helper from @callee_owned () -> (@unowned ()) to @callee_unowned @objc_block () -> (@unowned ()) + 39 at Signal.swift:26
frame #13: 0x00000001118439c4 libdispatch.dylib`_dispatch_client_callout + 8
frame #14: 0x000000011182d5f8 libdispatch.dylib`_dispatch_sync_f_invoke + 303
frame #15: 0x000000011132b21c ReactiveCocoa`ReactiveCocoa.Signal.current.getter : A(self=0x000000011e7c0000) + 412 at Signal.swift:26
frame #16: 0x0000000111334ec3 ReactiveCocoa`ReactiveCocoa.Signal.(value=Swift.Optional<T> at 0x00007fff50852800, self=0x000000011e7c0000).(closure #1) + 147 at Signal.swift:27
frame #17: 0x00000001113350f7 ReactiveCocoa`reabstraction thunk helper from @callee_owned () -> (@unowned ()) to @callee_unowned @objc_block () -> (@unowned ()) + 39 at Signal.swift:26
frame #18: 0x00000001118439c4 libdispatch.dylib`_dispatch_client_callout + 8
frame #19: 0x000000011182d5f8 libdispatch.dylib`_dispatch_sync_f_invoke + 303
frame #20: 0x000000011132b21c ReactiveCocoa`ReactiveCocoa.Signal.current.getter : A(self=0x000000011e7c0000) + 412 at Signal.swift:26
frame #21: 0x0000000111334ec3 ReactiveCocoa`ReactiveCocoa.Signal.(value=Swift.Optional<T> at 0x00007fff50852a60, self=0x000000011e7c0000).(closure #1) + 147 at Signal.swift:27
frame #22: 0x00000001113350f7 ReactiveCocoa`reabstraction thunk helper from @callee_owned () -> (@unowned ()) to @callee_unowned @objc_block () -> (@unowned ()) + 39 at Signal.swift:26
frame #23: 0x00000001118439c4 libdispatch.dylib`_dispatch_client_callout + 8
frame #24: 0x000000011182d5f8 libdispatch.dylib`_dispatch_sync_f_invoke + 303
frame #25: 0x000000011132b21c ReactiveCocoa`ReactiveCocoa.Signal.current.getter : A(self=0x000000011e7c0000) + 412 at Signal.swift:26
frame #26: 0x0000000111334ec3 ReactiveCocoa`ReactiveCocoa.Signal.(value=Swift.Optional<T> at 0x00007fff50852cc0, self=0x000000011e7c0000).(closure #1) + 147 at Signal.swift:27
frame #27: 0x00000001113350f7 ReactiveCocoa`reabstraction thunk helper from @callee_owned () -> (@unowned ()) to @callee_unowned @objc_block () -> (@unowned ()) + 39 at Signal.swift:26
frame #28: 0x00000001118439c4 libdispatch.dylib`_dispatch_client_callout + 8
frame #29: 0x000000011182d5f8 libdispatch.dylib`_dispatch_sync_f_invoke + 303
frame #30: 0x000000011132b21c ReactiveCocoa`ReactiveCocoa.Signal.current.getter : A(self=0x000000011e7c0000) + 412 at Signal.swift:26
frame #31: 0x0000000111334ec3 ReactiveCocoa`ReactiveCocoa.Signal.(value=Swift.Optional<T> at 0x00007fff50852f20, self=0x000000011e7c0000).(closure #1) + 147 at Signal.swift:27
frame #32: 0x00000001113350f7 ReactiveCocoa`reabstraction thunk helper from @callee_owned () -> (@unowned ()) to @callee_unowned @objc_block () -> (@unowned ()) + 39 at Signal.swift:26

The recursion is occurring in the access of self.current (rather than stream.current) in the definition of combineLatestWith:
(lldb) print stream
(ReactiveCocoa.Signal<String>) $R1 = 0x000000011dbd0058 {
  queue = 0x00007fb814a0fe30
  generator =
  _current = "\xe8\xda\xa0\x14\xb8\x7f"
  observers = {
    next = 140428596796008
    elements = {}
  }
}
(lldb) print self
(ReactiveCocoa.Signal<T>) $R2 = 0x000000011dbd0000 {
  queue = 0x00007fb814a0f0f0
  generator =
  observers = {
    next = 140428596796008
    elements = {}
  }
}

The Combined Signals are both SignalingProperties and take their initial values through the constructor, so I'd assume that the current property is a value rather than .None. Interestingly printing self in current omits the current value from the output, but it can be accessed directly and is shown to be initialized.
(lldb) print self
(ReactiveCocoa.Signal<T>) $R5 = 0x000000011dbd0000 {
  queue = 0x00007fb814a0f0f0
  generator =
  observers = {
    next = 140428596796008
    elements = {}
  }
}
(lldb) print self._current
(T?) $R7 =
Printing description of self._current:
(String?) _current = "\xe8\xda\xa0\x14\xb8\x7f"

This is under Beta 5 and I'm using the changes in another repo given the changes in B5, so this could be contributing to the behaviour. I thought this could be to do with the TODO comment about the raceyness, but think that applies to the observation rather than the recursion occuring in self.current.
I was wondering why the access of the private var would cause the current method to be called, but changing the name of _current to a different name still results in the overflow, so I'm all out of ideas
private var _annHog: T? = nil
private var observers = Bag<SinkOf<T>>()

/// The current (most recent) value of the Signal.
public var current: T {
    var value: T? = nil

    dispatch_sync(queue) {
        value = _annHog
    }

    return value!
}

Duplicate issue #1464: Buggy computation of Signal<T>.current in swift-development?
Created: 2014-08-15T14:36:36Z
Shouldn't this line read from
self._current
and not
self.current
?

  
    
      ReactiveCocoa/ReactiveCocoa/Signal.swift
    
    
         Line 27
      in
      e442b3c
    
  
  
    

        
          
           value = self.current

Closing comment (2014-08-15T18:11:59Z): Yes. Dupe of #1448.