Code change:
} // Write stores the given "val" in CycleState with the given "key". <del> // This function is not thread safe. In multi-threaded code, lock should be // acquired first. </del> <ins> // This function is thread safe by acquiring an internal lock first. </ins> func (c *CycleState) Write(key StateKey, val StateData) { <ins> c.mx.Lock() </ins> c.storage[key] = val <ins> c.mx.Unlock() </ins> } // Delete deletes data with the given key from CycleState. <del> // This function is not thread safe. In multi-threaded code, lock should be // acquired first. </del> <ins> // This function is thread safe by acquiring an internal lock first. </ins> func (c *CycleState) Delete(key StateKey) { <del> delete(c.storage, key) } // Lock acquires CycleState lock. func (c *CycleState) Lock() { </del> c.mx.Lock() <del> } // Unlock releases CycleState lock. func (c *CycleState) Unlock() { </del> <ins> delete(c.storage, key) </ins> c.mx.Unlock() } <del> // RLock acquires CycleState read lock. func (c *CycleState) RLock() { c.mx.RLock() } // RUnlock releases CycleState read lock. func (c *CycleState) RUnlock() { c.mx.RUnlock() } </del>

Related discussion:
text: if you want to mutate the top-level keyed value in CycleState, if it's a phase that CycleState is accessed consecutively, such as PreFilter, PreScore, it's safe to mutate CycleState in any manner if it's a phase that CycleState is accessed parallelly, to be race-free, (for now) you only have 1 option: keep the keyed value's pointer unchanged, and use an internal lock to mutate that pointer obj [TBD] we may come up with changes to add read locks to all in-tree plugins when reading values from CycleState, so that the out-of-tree plugin can surround its mutating logic between and - th mutating logic thereby can be any style Any thoughts on the TBD item? I would assume read locks wouldn't cost too much.
For now, I think the documentation is correct, it clearly says Read() and Write() are not thread-safe: The key thing here is whether in-tree plugin should use RLock/RUnlock or not. If yes, out-of-tree plugins can be freed to use Lock/Unlock. (see the TBD item in the previous comment)
I like the TBD, and optimizing for correctness in how in-tree plugins are accessing . I understand the concern with synchronization overhead, but I would be really surprised if it is the bottleneck in scheduling code.
It can be, when you have 5k-nodes clusters, or bigger.
I don't think it will be expensive, a rlock should be equivalent to an atomic add operation (basically it increments the number of readers). As a comparison, looking up the state from the map is likely to be more expensive.
Of course we are assuming no contention in the default plugins.
This is my impression as well. A vanilla version of scheduler doesn't impose write lock in Filter() and Score(), so should be good.
We can always see with the benchmarks and revert if its not negligible.
I will run the benchmarks to see if there is perf downgrade. /assign
Issues go stale after 90d of inactivity. Mark the issue as fresh with . Stale issues rot after an additional 30d of inactivity and eventually close. If this issue is safe to close now please do so with . Send feedback to sig-contributor-experience at . /lifecycle stale
/remove-lifecycle stale This is still relevant, yes?