Tcl Source Code

Ticket Change Details
Login
Overview

Artifact ID: 1d6bd02ad744807e8aca290566bc6c31d2c77f092d8a544eabdabe210bbb870d
Ticket: 080f846fd58958efeaa5e2247622dc0ee7fa1ff7
Channel system generating writable events BEFORE channel is open (refchan)
User & Date: pooryorick 2024-04-19 08:34:54
Changes

  1. icomment:
    To summarize again:
    
    	1) The "problem" described in this ticket is that a channel is being set to nonblocking before it is connected.  The only thing that should be done with a channel before it is actually connected is to wait for it to connect, so the real answer to this ticket is, "don't do that."  No example on the wiki has done that in the last 20 years.
    
    	2) Regardless of that argument, the code has now been changed so that setting a channel to non-blocking before it is connected now behaves as "expected".
    
    	3) After a channel event handler runs, it is necessary to somehow return again to the event loop before another handler runs so that the event loop can mediate the handling of events.  That's why the timer was introduced back in the fix for [67a5eabbd3].  Without that, reentrancy bugs happen.  The author of Atom recently wrote up a description of exactly the same problem in [https://zed.dev/blog/gpui-ownership|Ownership and data flow in GPUI]:
    
    	<blockquote>This simplicity, however, led to a subtle bug that went unnoticed until the code was widely used in production. The problem manifested when one listener function emitted an event to the same emitter it was subscribed to. This inadvertently triggered reentrancy, where the emitting function was called again before it had completed its execution. This recursive-like behavior contradicted our expectation of linear function execution and got us into an unexpected state. Even though JavaScript's garbage collector enforces memory safety, the language's relaxed ownership model made it easy for me to write this bug.
    
    	Rust's constraints make this naive approach to rather more difficult. We're strongly encouraged down a different path, which prevents the kind of reetrancy I described above. In GPUI, when you call emit or notify, no listeners are invoked. Instead, we push data to a queue of effects. At the end of each update we flush these effects, popping from the front of the queue until it becomes empty and then returning control to the event loop. Any effect handler can itself push more effects, but the system eventually quiesces. This gives us run-to-completion semantics without reentrancy bugs and plays nicely with Rust.</blockquote>
    
  2. login: "pooryorick"
  3. mimetype: "text/x-fossil-wiki"