Code Monkey home page Code Monkey logo

Comments (6)

eliekarouz avatar eliekarouz commented on July 28, 2024

@leandromperez,
It would be nice if you can isolate your code and post it here.

Could it be possible that this method: ... mutates concurrently the state, therefore the dictionary, creating duplicate entries?

No, this method executes blocks in a serial fashion.

from rxfeedback.swift.

leandromperez avatar leandromperez commented on July 28, 2024

Hi @eliekarouz,
Thanks for your answer.
This is a really condensed version of the code, there are many different parts to it, but this is mostly what's related to the feedback loop:


struct ClassesStateMachine{
   //This is the State
         ...    
}

extension ClassesStateMachine {
    typealias FeedbackLoop = (Driver<ClassesStateMachine>) -> Signal<ClassesStateMachine.Event>

    struct Feedback {

	//This is the system driver, a reduced version of it
        static func statemachineFeedbackDriver(_ interactor: ClassesInteractorImp) -> Driver<ClassesStateMachine> {
		
		return Driver.system(initialState: ClassesStateMachine.initialState,
				     reduce: ClassesStateMachine.reduce,
				     feedback:
						Feedback.uiFeedback(interactor),
						Feedback.processInteractions(interactor))
        }


        //This is the feedback that will analyze the state and respond with a service execution. E.g. when the user interacts with the screen, like tapping a button to execute some logic in the backend.
	//It returns a Set of Statemachine to avoid cancelling pending services of previous loops (i.e. interactios generated in previous Statemachines)
        private static func processInteractions(_ interactor: ClassesInteractorImp) -> ClassesStateMachine.FeedbackLoop {

            let interactionToProcess : (ClassesStateMachine) -> Set<ClassesStateMachine> = { statemachine in
                if statemachine.interactionToProcess != nil {
                    return Set([statemachine])
                }
                return Set()
            }

            let processInteraction : (ClassesStateMachine) -> Signal<ClassesStateMachine.Event> = { (statemachine) in
                return interactor.executeInteraction(of:statemachine)
            }

            return react(requests: interactionToProcess, effects: processInteraction)
        }

	//This binds te UI with the state
        private static func uiFeedback(_ interactor : ClassesInteractorImp) -> ClassesStateMachine.FeedbackLoop {

            return bind {[unowned interactor] stateMachine in

                let subscriptions = [
                    stateMachine.drive(interactor.presenter.statemachineInput),
                ]

                let events : [Signal<ClassesStateMachine.Event>] = [ interactor.viewController.userEvents]

                return Bindings(subscriptions: subscriptions, events: events)
            }
        }
    }
}

extension ClassesInteractorImp {

    //An interaction is an action that the user can perform on the screen, containing the necessary data to execute a service
    func executeInteraction( of statemachine:ClassesStateMachine) -> Signal<ClassesStateMachine.Event> {

        guard let interaction = statemachine.interactionToProcess else {fatalError("The statemachine must have an interaction")}

        let service = self.service(for:interaction, of: statemachine)

        return service.asSignal(onErrorRecover: { .just( ClassesStateMachine.Event.didFail(interaction, error:$0)) })
    }

    //This will perform a service, i.e. talk to the backend, and create an event when the response is obtained.
    func service(for interaction: ClassesStateMachine.Interaction, of statemachine: ClassesStateMachine) -> Observable<ClassesStateMachine.Event>{

        switch interaction {

        case .doSomething :
            return doSomething(with: statemachine) // talks to the backend to doSomething and returns an Event in response

            ...
        }
}

I was thinking that maybe more than one effect was mutating that dictionary concurrently, because I cannot think of another way of inserting a second object with an existing key into that dictionary.

We're not calling this function directly: public func react<State, Request: Equatable, RequestID, Event>( requests: @escaping (State) -> [RequestID: Request], effects: @escaping (_ initial: Request, _ state: Observable<Request>)

We're calling this one: public func react<State, Request, Event>( requests: @escaping (State) -> Set<Request>, effects: @escaping (Request) -> Signal<Event> )

I hope this information is useful somehow,
Thanks again,
Leandro

from rxfeedback.swift.

eliekarouz avatar eliekarouz commented on July 28, 2024

@leandromperez How are you declaring hashable/equatable for the ClassesStateMachine. Could it be that you have by mistake reference types that are being mutated AND used to compute the hash or ==

from rxfeedback.swift.

leandromperez avatar leandromperez commented on July 28, 2024
struct ClassesStateMachine {
    var id : Int
}

extension ClassesStateMachine: Equatable {}
func == (lhs: ClassesStateMachine, rhs: ClassesStateMachine) -> Bool {
    return lhs.id == rhs.id
}

Sometimes the reducer returns a StateMachine that doesn't mutate the id, to avoid repeating some effects in the next loop. I don't see how that could cause that crash, but do you think it might be a problem?

from rxfeedback.swift.

eliekarouz avatar eliekarouz commented on July 28, 2024

@leandromperez ,
Please check this line:
https://github.com/apple/swift/blob/f978cb0ba32acad5cc434331fa6a6b0c39ea9358/stdlib/public/core/NativeDictionary.swift#L449

Try to look in your code for classes (reference types) not enums or structs (value types)... Most probably you are mutating the variables inside these classes which is causing this crash.

FYI this code crashes with a similar error because the class XString is being mutated...

class XString: Hashable, Equatable {
    static func == (lhs: XString, rhs: XString) -> Bool {
        return lhs.string == rhs.string
    }

    var string: String

    init(_ string: String) {
        self.string = string
    }

    func hash(into hasher: inout Hasher) {
        hasher.combine(string)
    }
}

struct FakeValueType: Hashable, Equatable {
    let xstring: XString
}

func test() {
    let str1 = FakeValueType(xstring: XString("SomeString"))
    var dict = [str1: 0]
    for i in 0...100 {
        str1.xstring.string = "Mutation\(i) for SomeString"
        dict[str1] = i
    }
}

from rxfeedback.swift.

leandromperez avatar leandromperez commented on July 28, 2024

@eliekarouz That makes sense.

If I understood correctly, the problem comes when the struct (that's used as the dictionary key) is associated with a reference type instance, and when such instance is used to calculate the hash value.

I couldn't find a reference type instance inside the graph of associations that are used inside the hash function. But anyways, there might be one hidden somewhere I've missed.

So, I've changed the hashing code so it depends only on the identifier that's used inside the equals function. Hopefully, that will fix the issue.

I can close this ticket and re-open it if necessary,

Thanks for your help!

from rxfeedback.swift.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.